mirror of
https://github.com/bisq-network/bisq.git
synced 2024-11-19 01:41:11 +01:00
move network code to module
This commit is contained in:
parent
105a63847a
commit
c6ece486ed
2
.gitignore
vendored
2
.gitignore
vendored
@ -19,3 +19,5 @@ build
|
|||||||
.project
|
.project
|
||||||
.settings
|
.settings
|
||||||
*.java.hsp
|
*.java.hsp
|
||||||
|
|
||||||
|
gui/updatefx
|
@ -3,88 +3,9 @@
|
|||||||
<component name="ProjectCodeStyleSettingsManager">
|
<component name="ProjectCodeStyleSettingsManager">
|
||||||
<option name="PER_PROJECT_SETTINGS">
|
<option name="PER_PROJECT_SETTINGS">
|
||||||
<value>
|
<value>
|
||||||
<option name="LINE_SEPARATOR" value=" " />
|
|
||||||
<option name="CLASS_COUNT_TO_USE_IMPORT_ON_DEMAND" value="99" />
|
|
||||||
<option name="NAMES_COUNT_TO_USE_IMPORT_ON_DEMAND" value="2" />
|
|
||||||
<option name="PACKAGES_TO_USE_IMPORT_ON_DEMAND">
|
|
||||||
<value>
|
|
||||||
<package name="java.awt" withSubpackages="false" static="false" />
|
|
||||||
<package name="javafx.scene" withSubpackages="true" static="false" />
|
|
||||||
<package name="javax.swing" withSubpackages="false" static="false" />
|
|
||||||
<package name="org.junit.Assert" withSubpackages="true" static="true" />
|
|
||||||
</value>
|
|
||||||
</option>
|
|
||||||
<option name="IMPORT_LAYOUT_TABLE">
|
|
||||||
<value>
|
|
||||||
<package name="io.bitsquare" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="org.bitcoinj" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="com.google.common" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="com.google.gson" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="com.google.inject" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="java.awt" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="java.io" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="java.math" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="java.net" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="java.nio" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="java.security" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="java.text" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="java.util" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="javax.annotation" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="javax.inject" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="javax.swing" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="viewfx" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="javafx" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="com.sun.javafx" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="de.jensd.fx" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="net.glxn" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="net.tomp2p" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="org.controlsfx" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="org.jetbrains" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="org.junit" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="org.slf4j" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="org.spongycastle" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="" withSubpackages="true" static="false" />
|
|
||||||
<emptyLine />
|
|
||||||
<package name="" withSubpackages="true" static="true" />
|
|
||||||
</value>
|
|
||||||
</option>
|
|
||||||
<option name="RIGHT_MARGIN" value="160" />
|
|
||||||
<option name="WRAP_WHEN_TYPING_REACHES_RIGHT_MARGIN" value="true" />
|
|
||||||
<XML>
|
<XML>
|
||||||
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
|
||||||
</XML>
|
</XML>
|
||||||
<codeStyleSettings language="JAVA">
|
|
||||||
<option name="ELSE_ON_NEW_LINE" value="true" />
|
|
||||||
<option name="WRAP_LONG_LINES" value="true" />
|
|
||||||
<option name="FIELD_ANNOTATION_WRAP" value="0" />
|
|
||||||
</codeStyleSettings>
|
|
||||||
</value>
|
</value>
|
||||||
</option>
|
</option>
|
||||||
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
|
||||||
|
@ -1,9 +0,0 @@
|
|||||||
<component name="CopyrightManager">
|
|
||||||
<copyright>
|
|
||||||
<option name="notice" value="This file is part of Bitsquare. Bitsquare is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. Bitsquare is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for more details. You should have received a copy of the GNU Affero General Public License along with Bitsquare. If not, see <http://www.gnu.org/licenses/>." />
|
|
||||||
<option name="keyword" value="GNU Affero General Public License" />
|
|
||||||
<option name="allowReplaceKeyword" value="" />
|
|
||||||
<option name="myName" value="Bitsquare Affero GPLv3" />
|
|
||||||
<option name="myLocal" value="true" />
|
|
||||||
</copyright>
|
|
||||||
</component>
|
|
@ -1,3 +1,3 @@
|
|||||||
<component name="CopyrightManager">
|
<component name="CopyrightManager">
|
||||||
<settings default="Bitsquare Affero GPLv3" />
|
<settings default="" />
|
||||||
</component>
|
</component>
|
@ -1,157 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.app.bootstrap;
|
|
||||||
|
|
||||||
import io.bitsquare.app.Logging;
|
|
||||||
import io.bitsquare.app.Version;
|
|
||||||
import io.bitsquare.p2p.BootstrapNodes;
|
|
||||||
import io.bitsquare.p2p.Node;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import net.tomp2p.connection.ChannelClientConfiguration;
|
|
||||||
import net.tomp2p.connection.ChannelServerConfiguration;
|
|
||||||
import net.tomp2p.dht.PeerBuilderDHT;
|
|
||||||
import net.tomp2p.nat.PeerBuilderNAT;
|
|
||||||
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.PeerStatistic;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import io.netty.util.concurrent.DefaultEventExecutorGroup;
|
|
||||||
import org.springframework.core.env.Environment;
|
|
||||||
|
|
||||||
public class BootstrapNode {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(BootstrapNode.class);
|
|
||||||
|
|
||||||
private static Peer peer = null;
|
|
||||||
|
|
||||||
private final Environment env;
|
|
||||||
private boolean noPeersInfoPrinted;
|
|
||||||
|
|
||||||
public BootstrapNode(Environment env) {
|
|
||||||
this.env = env;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void start() {
|
|
||||||
BootstrapNodes bootstrapNodes = new BootstrapNodes();
|
|
||||||
int p2pId = env.getProperty(Node.P2P_ID_KEY, Integer.class, Node.REG_TEST_P2P_ID); // use regtest as default
|
|
||||||
bootstrapNodes.initWithNetworkId(p2pId);
|
|
||||||
String name = env.getProperty(Node.NAME_KEY, bootstrapNodes.getLocalhostNode().getName());
|
|
||||||
int port = env.getProperty(Node.PORT_KEY, Integer.class, bootstrapNodes.getLocalhostNode().getPort());
|
|
||||||
|
|
||||||
Logging.setup(name + "_" + port);
|
|
||||||
|
|
||||||
try {
|
|
||||||
Number160 peerId = Number160.createHash(name);
|
|
||||||
|
|
||||||
DefaultEventExecutorGroup eventExecutorGroup = new DefaultEventExecutorGroup(50);
|
|
||||||
ChannelClientConfiguration clientConf = PeerBuilder.createDefaultChannelClientConfiguration();
|
|
||||||
clientConf.pipelineFilter(new PeerBuilder.EventExecutorGroupFilter(eventExecutorGroup));
|
|
||||||
|
|
||||||
ChannelServerConfiguration serverConf = PeerBuilder.createDefaultChannelServerConfiguration();
|
|
||||||
serverConf.pipelineFilter(new PeerBuilder.EventExecutorGroupFilter(eventExecutorGroup));
|
|
||||||
serverConf.connectionTimeoutTCPMillis(5000);
|
|
||||||
|
|
||||||
peer = new PeerBuilder(peerId)
|
|
||||||
.ports(port)
|
|
||||||
.p2pId(p2pId)
|
|
||||||
.channelClientConfiguration(clientConf)
|
|
||||||
.channelServerConfiguration(serverConf)
|
|
||||||
.start();
|
|
||||||
|
|
||||||
/*peer.objectDataReply((sender, request) -> {
|
|
||||||
log.trace("received request: " + request.toString());
|
|
||||||
return "pong";
|
|
||||||
});*/
|
|
||||||
|
|
||||||
new PeerBuilderDHT(peer).start();
|
|
||||||
new PeerBuilderNAT(peer).start();
|
|
||||||
|
|
||||||
final int _port = port;
|
|
||||||
if (!name.equals(bootstrapNodes.getLocalhostNode().getName())) {
|
|
||||||
List<Node> bootstrapNodesExcludingMyself = bootstrapNodes.getBootstrapNodes().stream().filter(e -> !e.getName().equals
|
|
||||||
(name)).collect(Collectors.toList());
|
|
||||||
log.info("Bootstrapping to bootstrapNodes " + bootstrapNodesExcludingMyself);
|
|
||||||
long ts = System.currentTimeMillis();
|
|
||||||
List<PeerAddress> bootstrapAddressesExcludingMyself = bootstrapNodesExcludingMyself.stream()
|
|
||||||
.map(e -> e.toPeerAddressWithPort(_port)).collect(Collectors.toList());
|
|
||||||
peer.bootstrap().bootstrapTo(bootstrapAddressesExcludingMyself).start().awaitUninterruptibly();
|
|
||||||
log.info("Bootstrapping done after {} msec", System.currentTimeMillis() - ts);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.info("When using localhost we do not bootstrap to other nodes");
|
|
||||||
}
|
|
||||||
peer.peerBean().peerMap().addPeerMapChangeListener(new PeerMapChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void peerInserted(PeerAddress peerAddress, boolean verified) {
|
|
||||||
log.info("Peer inserted: peerAddress=" + peerAddress + ", verified=" + verified);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void peerRemoved(PeerAddress peerAddress, PeerStatistic peerStatistics) {
|
|
||||||
log.info("Peer removed: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void peerUpdated(PeerAddress peerAddress, PeerStatistic peerStatistics) {
|
|
||||||
//log.info("Peer updated: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
log.info("Bootstrap node started with name=" + name + " ,p2pId=" + p2pId + " ,port=" + port +
|
|
||||||
" and network protocol version=" + Version.NETWORK_PROTOCOL_VERSION);
|
|
||||||
new Thread(() -> {
|
|
||||||
while (true) {
|
|
||||||
if (peer.peerBean().peerMap().all().size() > 0) {
|
|
||||||
noPeersInfoPrinted = false;
|
|
||||||
int relayed = 0;
|
|
||||||
for (PeerAddress peerAddress : peer.peerBean().peerMap().all()) {
|
|
||||||
log.info("Peer: " + peerAddress.toString());
|
|
||||||
if (peerAddress.isRelayed())
|
|
||||||
relayed++;
|
|
||||||
}
|
|
||||||
log.info("Number of peers online = " + peer.peerBean().peerMap().all().size());
|
|
||||||
log.info("Relayed peers = " + relayed);
|
|
||||||
}
|
|
||||||
else if (noPeersInfoPrinted) {
|
|
||||||
log.info("No peers online");
|
|
||||||
noPeersInfoPrinted = true;
|
|
||||||
}
|
|
||||||
try {
|
|
||||||
Thread.sleep(10000);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}).start();
|
|
||||||
|
|
||||||
} catch (Throwable t) {
|
|
||||||
log.error("Fatal exception " + t.getMessage());
|
|
||||||
if (peer != null)
|
|
||||||
peer.shutdown().awaitUninterruptibly();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,52 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.app.bootstrap;
|
|
||||||
|
|
||||||
import io.bitsquare.app.BitsquareEnvironment;
|
|
||||||
import io.bitsquare.app.BitsquareExecutable;
|
|
||||||
import io.bitsquare.p2p.BootstrapNodes;
|
|
||||||
import io.bitsquare.p2p.Node;
|
|
||||||
|
|
||||||
import joptsimple.OptionParser;
|
|
||||||
import joptsimple.OptionSet;
|
|
||||||
|
|
||||||
public class BootstrapNodeMain extends BitsquareExecutable {
|
|
||||||
|
|
||||||
public static void main(String[] args) throws Exception {
|
|
||||||
new BootstrapNodeMain().execute(args);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void customizeOptionParsing(OptionParser parser) {
|
|
||||||
BootstrapNodes bootstrapNodes = new BootstrapNodes();
|
|
||||||
bootstrapNodes.initWithNetworkId(Node.REG_TEST_P2P_ID); // use regtest as default
|
|
||||||
parser.accepts(Node.NAME_KEY, description("Name of this node", bootstrapNodes.getLocalhostNode().getName()))
|
|
||||||
.withRequiredArg()
|
|
||||||
.ofType(String.class);
|
|
||||||
parser.accepts(Node.P2P_ID_KEY, description("P2P network ID",
|
|
||||||
bootstrapNodes.getLocalhostNode().getP2pId()))
|
|
||||||
.withRequiredArg()
|
|
||||||
.ofType(int.class);
|
|
||||||
parser.accepts(Node.PORT_KEY, description("Port to listen on", bootstrapNodes.getLocalhostNode().getPort()))
|
|
||||||
.withRequiredArg()
|
|
||||||
.ofType(int.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void doExecute(OptionSet options) {
|
|
||||||
new BootstrapNode(new BitsquareEnvironment(options)).start();
|
|
||||||
}
|
|
||||||
}
|
|
@ -23,8 +23,10 @@
|
|||||||
</encoder>
|
</encoder>
|
||||||
</appender>
|
</appender>
|
||||||
|
|
||||||
<root level="INFO">
|
<root level="TRACE">
|
||||||
<appender-ref ref="CONSOLE_APPENDER"/>
|
<appender-ref ref="CONSOLE_APPENDER"/>
|
||||||
</root>
|
</root>
|
||||||
|
|
||||||
|
<logger name="com.msopentech.thali.toronionproxy.OnionProxyManagerEventHandler" level="WARN"/>
|
||||||
|
|
||||||
</configuration>
|
</configuration>
|
||||||
|
@ -17,18 +17,14 @@
|
|||||||
|
|
||||||
package io.bitsquare.app;
|
package io.bitsquare.app;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import ch.qos.logback.classic.LoggerContext;
|
import ch.qos.logback.classic.LoggerContext;
|
||||||
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
|
import ch.qos.logback.classic.encoder.PatternLayoutEncoder;
|
||||||
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
|
import ch.qos.logback.core.rolling.FixedWindowRollingPolicy;
|
||||||
import ch.qos.logback.core.rolling.RollingFileAppender;
|
import ch.qos.logback.core.rolling.RollingFileAppender;
|
||||||
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
|
import ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
public class Logging {
|
public class Logging {
|
||||||
private static final Logger log = LoggerFactory.getLogger(Logging.class);
|
|
||||||
|
|
||||||
public static void setup(String fileName) {
|
public static void setup(String fileName) {
|
||||||
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
|
LoggerContext loggerContext = (LoggerContext) LoggerFactory.getILoggerFactory();
|
||||||
|
|
@ -15,15 +15,11 @@
|
|||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.bitsquare.crypto;
|
package io.bitsquare.common.crypto;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class CryptoException extends Exception {
|
public class CryptoException extends Exception {
|
||||||
private static final Logger log = LoggerFactory.getLogger(CryptoException.class);
|
|
||||||
|
|
||||||
public CryptoException() {
|
private CryptoException() {
|
||||||
}
|
}
|
||||||
|
|
||||||
public CryptoException(String message) {
|
public CryptoException(String message) {
|
||||||
@ -38,7 +34,7 @@ public class CryptoException extends Exception {
|
|||||||
super(cause);
|
super(cause);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CryptoException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
private CryptoException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
|
||||||
super(message, cause, enableSuppression, writableStackTrace);
|
super(message, cause, enableSuppression, writableStackTrace);
|
||||||
}
|
}
|
||||||
}
|
}
|
136
common/src/main/java/io/bitsquare/common/crypto/CryptoUtil.java
Normal file
136
common/src/main/java/io/bitsquare/common/crypto/CryptoUtil.java
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/*
|
||||||
|
* This file is part of Bitsquare.
|
||||||
|
*
|
||||||
|
* Bitsquare is free software: you can redistribute it and/or modify it
|
||||||
|
* under the terms of the GNU Affero General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or (at
|
||||||
|
* your option) any later version.
|
||||||
|
*
|
||||||
|
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
||||||
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
||||||
|
* License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Affero General Public License
|
||||||
|
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package io.bitsquare.common.crypto;
|
||||||
|
|
||||||
|
import io.bitsquare.common.util.Utilities;
|
||||||
|
import org.bitcoinj.core.Sha256Hash;
|
||||||
|
import org.bitcoinj.core.Utils;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.bouncycastle.util.encoders.Base64;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
import java.security.*;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
|
||||||
|
public class CryptoUtil {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(CryptoUtil.class);
|
||||||
|
public static final String STORAGE_SIGN_KEY_ALGO = "DSA";
|
||||||
|
public static final String MSG_SIGN_KEY_ALGO = "DSA";
|
||||||
|
public static final String MSG_ENCR_KEY_ALGO = "RSA";
|
||||||
|
|
||||||
|
public static final String SYM_ENCR_KEY_ALGO = "AES";
|
||||||
|
public static final String SYM_CIPHER = "AES";
|
||||||
|
public static final String ASYM_CIPHER = "RSA"; //RSA/ECB/PKCS1Padding
|
||||||
|
public static final String MSG_SIGN_ALGO = "SHA1withDSA";
|
||||||
|
|
||||||
|
|
||||||
|
public static KeyPair generateStorageSignatureKeyPair() throws NoSuchAlgorithmException {
|
||||||
|
long ts = System.currentTimeMillis();
|
||||||
|
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(STORAGE_SIGN_KEY_ALGO);
|
||||||
|
keyPairGenerator.initialize(1024);
|
||||||
|
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||||
|
log.trace("Generate storageSignatureKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||||
|
return keyPair;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static KeyPair generateMsgSignatureKeyPair() throws NoSuchAlgorithmException {
|
||||||
|
long ts = System.currentTimeMillis();
|
||||||
|
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(MSG_SIGN_KEY_ALGO);
|
||||||
|
keyPairGenerator.initialize(1024);
|
||||||
|
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||||
|
log.trace("Generate msgSignatureKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||||
|
return keyPair;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static KeyPair generateMsgEncryptionKeyPair() throws NoSuchAlgorithmException {
|
||||||
|
long ts = System.currentTimeMillis();
|
||||||
|
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(MSG_ENCR_KEY_ALGO);
|
||||||
|
keyPairGenerator.initialize(2048);
|
||||||
|
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
||||||
|
log.trace("Generate msgEncryptionKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
||||||
|
return keyPair;
|
||||||
|
}
|
||||||
|
|
||||||
|
static {
|
||||||
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String signMessage(PrivateKey privateKey, String message)
|
||||||
|
throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
|
||||||
|
Signature sig = Signature.getInstance(MSG_SIGN_ALGO);
|
||||||
|
sig.initSign(privateKey);
|
||||||
|
sig.update(message.getBytes());
|
||||||
|
return Base64.toBase64String(sig.sign());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean verifyMessage(PublicKey publicKey, String message, String signature)
|
||||||
|
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
||||||
|
Signature sig = Signature.getInstance(MSG_SIGN_ALGO);
|
||||||
|
sig.initVerify(publicKey);
|
||||||
|
sig.update(message.getBytes());
|
||||||
|
return sig.verify(Base64.decode(signature));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] signStorageData(PrivateKey privateKey, byte[] data)
|
||||||
|
throws SignatureException, NoSuchAlgorithmException, InvalidKeyException {
|
||||||
|
Signature sig = Signature.getInstance(MSG_SIGN_ALGO);
|
||||||
|
sig.initSign(privateKey);
|
||||||
|
sig.update(data);
|
||||||
|
return sig.sign();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean verifyStorageData(PublicKey publicKey, byte[] data, byte[] signature)
|
||||||
|
throws NoSuchAlgorithmException, InvalidKeyException, SignatureException {
|
||||||
|
Signature sig = Signature.getInstance(MSG_SIGN_ALGO);
|
||||||
|
sig.initVerify(publicKey);
|
||||||
|
sig.update(data);
|
||||||
|
return sig.verify(signature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] getHash(Integer data) {
|
||||||
|
return Sha256Hash.hash(ByteBuffer.allocate(4).putInt(data).array());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] getHash(Object data) {
|
||||||
|
return Sha256Hash.hash(Utilities.objectToByteArray(data));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] getHash(String message) {
|
||||||
|
return Sha256Hash.hash(Utils.formatMessageForSigning(message));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getHashAsHex(String text) {
|
||||||
|
return Utils.HEX.encode(Sha256Hash.hash(Utils.formatMessageForSigning(text)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String pubKeyToString(PublicKey publicKey) {
|
||||||
|
final X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
|
||||||
|
return java.util.Base64.getEncoder().encodeToString(x509EncodedKeySpec.getEncoded());
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO just temp for arbitrator
|
||||||
|
public static PublicKey decodeDSAPubKeyHex(String pubKeyHex) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||||
|
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Utils.HEX.decode(pubKeyHex));
|
||||||
|
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
|
||||||
|
return keyFactory.generatePublic(pubKeySpec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -15,21 +15,20 @@
|
|||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.bitsquare.crypto;
|
package io.bitsquare.common.crypto;
|
||||||
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import java.security.KeyPair;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
public class KeyRing {
|
public class KeyRing {
|
||||||
private static final Logger log = LoggerFactory.getLogger(KeyRing.class);
|
private static final Logger log = LoggerFactory.getLogger(KeyRing.class);
|
||||||
|
|
||||||
// Public key is used as ID in DHT network. Used for data protection mechanism in TomP2P DHT
|
// Used for signing storage data
|
||||||
private KeyPair dhtSignatureKeyPair;
|
private KeyPair storageSignatureKeyPair;
|
||||||
// Used for signing messages sent over the wire
|
// Used for signing messages sent over the wire
|
||||||
private KeyPair msgSignatureKeyPair;
|
private KeyPair msgSignatureKeyPair;
|
||||||
// Used for encrypting messages sent over the wire (hybrid encryption scheme is used, so it is used only to encrypt a symmetric session key)
|
// Used for encrypting messages sent over the wire (hybrid encryption scheme is used, so it is used only to encrypt a symmetric session key)
|
||||||
@ -46,18 +45,17 @@ public class KeyRing {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// consider extra thread for loading (takes about 264 ms at first startup, then load only takes nearly no time)
|
// consider extra thread for loading (takes about 264 ms at first startup, then load only takes nearly no time)
|
||||||
public void init(KeyStorage keyStorage) throws CryptoException {
|
private void init(KeyStorage keyStorage) throws CryptoException {
|
||||||
if (keyStorage.allKeyFilesExist()) {
|
if (keyStorage.allKeyFilesExist()) {
|
||||||
dhtSignatureKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.DHT_SIGNATURE);
|
storageSignatureKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.STORAGE_SIGNATURE);
|
||||||
msgSignatureKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.MSG_SIGNATURE);
|
msgSignatureKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.MSG_SIGNATURE);
|
||||||
msgEncryptionKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.MSG_ENCRYPTION);
|
msgEncryptionKeyPair = keyStorage.loadKeyPair(KeyStorage.Key.MSG_ENCRYPTION);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// First time we create key pairs
|
// First time we create key pairs
|
||||||
try {
|
try {
|
||||||
this.dhtSignatureKeyPair = CryptoService.generateDhtSignatureKeyPair();
|
this.storageSignatureKeyPair = CryptoUtil.generateStorageSignatureKeyPair();
|
||||||
this.msgSignatureKeyPair = CryptoService.generateMsgSignatureKeyPair();
|
this.msgSignatureKeyPair = CryptoUtil.generateMsgSignatureKeyPair();
|
||||||
this.msgEncryptionKeyPair = CryptoService.generateMsgEncryptionKeyPair();
|
this.msgEncryptionKeyPair = CryptoUtil.generateMsgEncryptionKeyPair();
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw new CryptoException("Error at KeyRing constructor ", e);
|
throw new CryptoException("Error at KeyRing constructor ", e);
|
||||||
@ -65,28 +63,28 @@ public class KeyRing {
|
|||||||
keyStorage.saveKeyRing(this);
|
keyStorage.saveKeyRing(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
pubKeyRing = new PubKeyRing(dhtSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic());
|
pubKeyRing = new PubKeyRing(storageSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic());
|
||||||
}
|
}
|
||||||
|
|
||||||
// For unit testing
|
// For unit testing
|
||||||
KeyRing() throws NoSuchAlgorithmException {
|
KeyRing() throws NoSuchAlgorithmException {
|
||||||
keyStorage = null;
|
keyStorage = null;
|
||||||
this.dhtSignatureKeyPair = CryptoService.generateDhtSignatureKeyPair();
|
this.storageSignatureKeyPair = CryptoUtil.generateStorageSignatureKeyPair();
|
||||||
this.msgSignatureKeyPair = CryptoService.generateMsgSignatureKeyPair();
|
this.msgSignatureKeyPair = CryptoUtil.generateMsgSignatureKeyPair();
|
||||||
this.msgEncryptionKeyPair = CryptoService.generateMsgEncryptionKeyPair();
|
this.msgEncryptionKeyPair = CryptoUtil.generateMsgEncryptionKeyPair();
|
||||||
pubKeyRing = new PubKeyRing(dhtSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic());
|
pubKeyRing = new PubKeyRing(storageSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic());
|
||||||
}
|
}
|
||||||
|
|
||||||
KeyRing(KeyPair dhtSignatureKeyPair, KeyPair msgSignatureKeyPair, KeyPair msgEncryptionKeyPair) {
|
KeyRing(KeyPair storageSignatureKeyPair, KeyPair msgSignatureKeyPair, KeyPair msgEncryptionKeyPair) {
|
||||||
this.keyStorage = null;
|
this.keyStorage = null;
|
||||||
this.dhtSignatureKeyPair = dhtSignatureKeyPair;
|
this.storageSignatureKeyPair = storageSignatureKeyPair;
|
||||||
this.msgSignatureKeyPair = msgSignatureKeyPair;
|
this.msgSignatureKeyPair = msgSignatureKeyPair;
|
||||||
this.msgEncryptionKeyPair = msgEncryptionKeyPair;
|
this.msgEncryptionKeyPair = msgEncryptionKeyPair;
|
||||||
pubKeyRing = new PubKeyRing(dhtSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic());
|
pubKeyRing = new PubKeyRing(storageSignatureKeyPair.getPublic(), msgSignatureKeyPair.getPublic(), msgEncryptionKeyPair.getPublic());
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyPair getDhtSignatureKeyPair() {
|
public KeyPair getStorageSignatureKeyPair() {
|
||||||
return dhtSignatureKeyPair;
|
return storageSignatureKeyPair;
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyPair getMsgSignatureKeyPair() {
|
public KeyPair getMsgSignatureKeyPair() {
|
@ -15,34 +15,25 @@
|
|||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.bitsquare.crypto;
|
package io.bitsquare.common.crypto;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
|
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Named;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.FileOutputStream;
|
import java.io.FileOutputStream;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.*;
|
||||||
import java.security.KeyFactory;
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.KeyStoreException;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PrivateKey;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.Security;
|
|
||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.spec.InvalidKeySpecException;
|
import java.security.spec.InvalidKeySpecException;
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
import java.security.spec.PKCS8EncodedKeySpec;
|
||||||
import java.security.spec.X509EncodedKeySpec;
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
|
||||||
|
|
||||||
public class KeyStorage {
|
public class KeyStorage {
|
||||||
private static final Logger log = LoggerFactory.getLogger(KeyStorage.class);
|
private static final Logger log = LoggerFactory.getLogger(KeyStorage.class);
|
||||||
|
|
||||||
@ -52,11 +43,10 @@ public class KeyStorage {
|
|||||||
Security.addProvider(new BouncyCastleProvider());
|
Security.addProvider(new BouncyCastleProvider());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public enum Key {
|
public enum Key {
|
||||||
DHT_SIGNATURE("dhtSignature", CryptoService.DHT_SIGN_KEY_ALGO),
|
STORAGE_SIGNATURE("storageSignature", CryptoUtil.STORAGE_SIGN_KEY_ALGO),
|
||||||
MSG_SIGNATURE("msgSignature", CryptoService.MSG_SIGN_KEY_ALGO),
|
MSG_SIGNATURE("msgSignature", CryptoUtil.MSG_SIGN_KEY_ALGO),
|
||||||
MSG_ENCRYPTION("msgEncryption", CryptoService.MSG_ENCR_KEY_ALGO);
|
MSG_ENCRYPTION("msgEncryption", CryptoUtil.MSG_ENCR_KEY_ALGO);
|
||||||
|
|
||||||
private final String fileName;
|
private final String fileName;
|
||||||
private final String algorithm;
|
private final String algorithm;
|
||||||
@ -74,6 +64,7 @@ public class KeyStorage {
|
|||||||
return algorithm;
|
return algorithm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NotNull
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Key{" +
|
return "Key{" +
|
||||||
@ -91,15 +82,15 @@ public class KeyStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public boolean allKeyFilesExist() throws CryptoException {
|
public boolean allKeyFilesExist() throws CryptoException {
|
||||||
return fileExists(KeyStorage.Key.DHT_SIGNATURE) && fileExists(KeyStorage.Key.MSG_SIGNATURE) && fileExists(KeyStorage.Key.MSG_ENCRYPTION);
|
return fileExists(KeyStorage.Key.STORAGE_SIGNATURE) && fileExists(KeyStorage.Key.MSG_SIGNATURE) && fileExists(KeyStorage.Key.MSG_ENCRYPTION);
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean fileExists(Key key) throws CryptoException {
|
private boolean fileExists(Key key) {
|
||||||
return new File(storageDir + "/" + key.getFileName() + "Pub.key").exists();
|
return new File(storageDir + "/" + key.getFileName() + "Pub.key").exists();
|
||||||
}
|
}
|
||||||
|
|
||||||
public KeyPair loadKeyPair(Key key) throws CryptoException {
|
public KeyPair loadKeyPair(Key key) throws CryptoException {
|
||||||
long now = System.currentTimeMillis();
|
// long now = System.currentTimeMillis();
|
||||||
try {
|
try {
|
||||||
KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm());
|
KeyFactory keyFactory = KeyFactory.getInstance(key.getAlgorithm());
|
||||||
PublicKey publicKey;
|
PublicKey publicKey;
|
||||||
@ -130,7 +121,7 @@ public class KeyStorage {
|
|||||||
log.error(e.getMessage());
|
log.error(e.getMessage());
|
||||||
throw new CryptoException("Could not load key " + key.toString(), e);
|
throw new CryptoException("Could not load key " + key.toString(), e);
|
||||||
}
|
}
|
||||||
log.info("load completed in {} msec", System.currentTimeMillis() - now);
|
//log.info("load completed in {} msec", System.currentTimeMillis() - now);
|
||||||
return new KeyPair(publicKey, privateKey);
|
return new KeyPair(publicKey, privateKey);
|
||||||
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
} catch (NoSuchAlgorithmException e) {
|
||||||
@ -141,7 +132,7 @@ public class KeyStorage {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void saveKeyRing(KeyRing keyRing) throws CryptoException {
|
public void saveKeyRing(KeyRing keyRing) throws CryptoException {
|
||||||
saveKeyPair(keyRing.getDhtSignatureKeyPair(), Key.DHT_SIGNATURE.getFileName());
|
saveKeyPair(keyRing.getStorageSignatureKeyPair(), Key.STORAGE_SIGNATURE.getFileName());
|
||||||
saveKeyPair(keyRing.getMsgSignatureKeyPair(), Key.MSG_SIGNATURE.getFileName());
|
saveKeyPair(keyRing.getMsgSignatureKeyPair(), Key.MSG_SIGNATURE.getFileName());
|
||||||
saveKeyPair(keyRing.getMsgEncryptionKeyPair(), Key.MSG_ENCRYPTION.getFileName());
|
saveKeyPair(keyRing.getMsgEncryptionKeyPair(), Key.MSG_ENCRYPTION.getFileName());
|
||||||
}
|
}
|
@ -15,24 +15,20 @@
|
|||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.bitsquare.crypto;
|
package io.bitsquare.common.crypto;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import java.security.KeyFactory;
|
import java.security.KeyFactory;
|
||||||
import java.security.MessageDigest;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.spec.InvalidKeySpecException;
|
import java.security.spec.InvalidKeySpecException;
|
||||||
import java.security.spec.X509EncodedKeySpec;
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as KeyRing but with public keys only.
|
* Same as KeyRing but with public keys only.
|
||||||
* Used to sent over the wire to other peer.
|
* Used to sent over the wire to other peer.
|
||||||
@ -41,43 +37,43 @@ public class PubKeyRing implements Serializable {
|
|||||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
// That object is sent over the wire, so we need to take care of version compatibility.
|
||||||
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
|
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(PubKeyRing.class);
|
transient private static final Logger log = LoggerFactory.getLogger(PubKeyRing.class);
|
||||||
|
|
||||||
private final byte[] dhtSignaturePubKeyBytes;
|
private final byte[] storageSignaturePubKeyBytes;
|
||||||
private final byte[] msgSignaturePubKeyBytes;
|
private final byte[] msgSignaturePubKeyBytes;
|
||||||
private final byte[] msgEncryptionPubKeyBytes;
|
private final byte[] msgEncryptionPubKeyBytes;
|
||||||
|
|
||||||
transient private PublicKey dhtSignaturePubKey;
|
transient private PublicKey storageSignaturePubKey;
|
||||||
transient private PublicKey msgSignaturePubKey;
|
transient private PublicKey msgSignaturePubKey;
|
||||||
transient private PublicKey msgEncryptionPubKey;
|
transient private PublicKey msgEncryptionPubKey;
|
||||||
|
|
||||||
public PubKeyRing(PublicKey dhtSignaturePubKey, PublicKey msgSignaturePubKey, PublicKey msgEncryptionPubKey) {
|
public PubKeyRing(PublicKey storageSignaturePubKey, PublicKey msgSignaturePubKey, PublicKey msgEncryptionPubKey) {
|
||||||
this.dhtSignaturePubKey = dhtSignaturePubKey;
|
this.storageSignaturePubKey = storageSignaturePubKey;
|
||||||
this.msgSignaturePubKey = msgSignaturePubKey;
|
this.msgSignaturePubKey = msgSignaturePubKey;
|
||||||
this.msgEncryptionPubKey = msgEncryptionPubKey;
|
this.msgEncryptionPubKey = msgEncryptionPubKey;
|
||||||
|
|
||||||
this.dhtSignaturePubKeyBytes = new X509EncodedKeySpec(dhtSignaturePubKey.getEncoded()).getEncoded();
|
this.storageSignaturePubKeyBytes = new X509EncodedKeySpec(storageSignaturePubKey.getEncoded()).getEncoded();
|
||||||
this.msgSignaturePubKeyBytes = new X509EncodedKeySpec(msgSignaturePubKey.getEncoded()).getEncoded();
|
this.msgSignaturePubKeyBytes = new X509EncodedKeySpec(msgSignaturePubKey.getEncoded()).getEncoded();
|
||||||
this.msgEncryptionPubKeyBytes = new X509EncodedKeySpec(msgEncryptionPubKey.getEncoded()).getEncoded();
|
this.msgEncryptionPubKeyBytes = new X509EncodedKeySpec(msgEncryptionPubKey.getEncoded()).getEncoded();
|
||||||
}
|
}
|
||||||
|
|
||||||
public PublicKey getDhtSignaturePubKey() {
|
public PublicKey getStorageSignaturePubKey() {
|
||||||
if (dhtSignaturePubKey == null) {
|
if (storageSignaturePubKey == null) {
|
||||||
try {
|
try {
|
||||||
dhtSignaturePubKey = KeyFactory.getInstance(CryptoService.DHT_SIGN_KEY_ALGO).generatePublic(new X509EncodedKeySpec(dhtSignaturePubKeyBytes));
|
storageSignaturePubKey = KeyFactory.getInstance(CryptoUtil.STORAGE_SIGN_KEY_ALGO).generatePublic(new X509EncodedKeySpec(storageSignaturePubKeyBytes));
|
||||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.error(e.getMessage());
|
log.error(e.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dhtSignaturePubKey;
|
return storageSignaturePubKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public PublicKey getMsgSignaturePubKey() {
|
public PublicKey getMsgSignaturePubKey() {
|
||||||
if (msgSignaturePubKey == null) {
|
if (msgSignaturePubKey == null) {
|
||||||
try {
|
try {
|
||||||
msgSignaturePubKey = KeyFactory.getInstance(CryptoService.MSG_SIGN_KEY_ALGO).generatePublic(new X509EncodedKeySpec(msgSignaturePubKeyBytes));
|
msgSignaturePubKey = KeyFactory.getInstance(CryptoUtil.MSG_SIGN_KEY_ALGO).generatePublic(new X509EncodedKeySpec(msgSignaturePubKeyBytes));
|
||||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.error(e.getMessage());
|
log.error(e.getMessage());
|
||||||
@ -89,7 +85,7 @@ public class PubKeyRing implements Serializable {
|
|||||||
public PublicKey getMsgEncryptionPubKey() {
|
public PublicKey getMsgEncryptionPubKey() {
|
||||||
if (msgEncryptionPubKey == null) {
|
if (msgEncryptionPubKey == null) {
|
||||||
try {
|
try {
|
||||||
msgEncryptionPubKey = KeyFactory.getInstance(CryptoService.MSG_ENCR_KEY_ALGO).generatePublic(new X509EncodedKeySpec(msgEncryptionPubKeyBytes));
|
msgEncryptionPubKey = KeyFactory.getInstance(CryptoUtil.MSG_ENCR_KEY_ALGO).generatePublic(new X509EncodedKeySpec(msgEncryptionPubKeyBytes));
|
||||||
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
} catch (InvalidKeySpecException | NoSuchAlgorithmException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.error(e.getMessage());
|
log.error(e.getMessage());
|
||||||
@ -105,7 +101,7 @@ public class PubKeyRing implements Serializable {
|
|||||||
|
|
||||||
PubKeyRing that = (PubKeyRing) o;
|
PubKeyRing that = (PubKeyRing) o;
|
||||||
|
|
||||||
if (!Arrays.equals(dhtSignaturePubKeyBytes, that.dhtSignaturePubKeyBytes)) return false;
|
if (!Arrays.equals(storageSignaturePubKeyBytes, that.storageSignaturePubKeyBytes)) return false;
|
||||||
if (!Arrays.equals(msgSignaturePubKeyBytes, that.msgSignaturePubKeyBytes)) return false;
|
if (!Arrays.equals(msgSignaturePubKeyBytes, that.msgSignaturePubKeyBytes)) return false;
|
||||||
return Arrays.equals(msgEncryptionPubKeyBytes, that.msgEncryptionPubKeyBytes);
|
return Arrays.equals(msgEncryptionPubKeyBytes, that.msgEncryptionPubKeyBytes);
|
||||||
|
|
||||||
@ -113,31 +109,18 @@ public class PubKeyRing implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
int result = dhtSignaturePubKeyBytes != null ? Arrays.hashCode(dhtSignaturePubKeyBytes) : 0;
|
int result = storageSignaturePubKeyBytes != null ? Arrays.hashCode(storageSignaturePubKeyBytes) : 0;
|
||||||
result = 31 * result + (msgSignaturePubKeyBytes != null ? Arrays.hashCode(msgSignaturePubKeyBytes) : 0);
|
result = 31 * result + (msgSignaturePubKeyBytes != null ? Arrays.hashCode(msgSignaturePubKeyBytes) : 0);
|
||||||
result = 31 * result + (msgEncryptionPubKeyBytes != null ? Arrays.hashCode(msgEncryptionPubKeyBytes) : 0);
|
result = 31 * result + (msgEncryptionPubKeyBytes != null ? Arrays.hashCode(msgEncryptionPubKeyBytes) : 0);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public String getHashString() {
|
|
||||||
try {
|
|
||||||
MessageDigest messageDigest = MessageDigest.getInstance("SHA-256");
|
|
||||||
messageDigest.update(dhtSignaturePubKeyBytes);
|
|
||||||
messageDigest.update(msgSignaturePubKeyBytes);
|
|
||||||
messageDigest.update(msgEncryptionPubKeyBytes);
|
|
||||||
return new String(messageDigest.digest());
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new RuntimeException("Hash Algorithm not found.", e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "PubKeyRing{" +
|
return "PubKeyRing{" +
|
||||||
"\ndhtSignaturePubKey=\n" + Util.pubKeyToString(getDhtSignaturePubKey()) +
|
"\nstorageSignaturePubKey=\n" + CryptoUtil.pubKeyToString(getStorageSignaturePubKey()) +
|
||||||
"\n\nmsgSignaturePubKey=\n" + Util.pubKeyToString(getMsgSignaturePubKey()) +
|
"\n\nmsgSignaturePubKey=\n" + CryptoUtil.pubKeyToString(getMsgSignaturePubKey()) +
|
||||||
"\n\nmsgEncryptionPubKey=\n" + Util.pubKeyToString(getMsgEncryptionPubKey()) +
|
"\n\nmsgEncryptionPubKey=\n" + CryptoUtil.pubKeyToString(getMsgEncryptionPubKey()) +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
@ -1,12 +1,9 @@
|
|||||||
package io.bitsquare.util;
|
package io.bitsquare.common.util;
|
||||||
|
|
||||||
import java.awt.*;
|
import java.awt.*;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -154,8 +151,7 @@ class DesktopUtil {
|
|||||||
if (retval == 0) {
|
if (retval == 0) {
|
||||||
logErr("Process ended immediately.");
|
logErr("Process ended immediately.");
|
||||||
return false;
|
return false;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
logErr("Process crashed.");
|
logErr("Process crashed.");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -221,7 +217,7 @@ class DesktopUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static EnumOS getOs() {
|
private static EnumOS getOs() {
|
||||||
|
|
||||||
String s = System.getProperty("os.name").toLowerCase();
|
String s = System.getProperty("os.name").toLowerCase();
|
||||||
|
|
||||||
@ -247,8 +243,7 @@ class DesktopUtil {
|
|||||||
|
|
||||||
if (s.contains("unix")) {
|
if (s.contains("unix")) {
|
||||||
return EnumOS.linux;
|
return EnumOS.linux;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return EnumOS.unknown;
|
return EnumOS.unknown;
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -15,43 +15,24 @@
|
|||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package io.bitsquare.util;
|
package io.bitsquare.common.util;
|
||||||
|
|
||||||
import io.bitsquare.common.handlers.ResultHandler;
|
|
||||||
|
|
||||||
import org.bitcoinj.utils.Threading;
|
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
import com.google.common.base.Charsets;
|
||||||
import com.google.common.io.CharStreams;
|
import com.google.common.io.CharStreams;
|
||||||
|
import com.google.gson.*;
|
||||||
import com.google.gson.FieldNamingPolicy;
|
import javafx.scene.input.Clipboard;
|
||||||
import com.google.gson.Gson;
|
import javafx.scene.input.ClipboardContent;
|
||||||
import com.google.gson.GsonBuilder;
|
import javafx.scene.web.WebEngine;
|
||||||
|
|
||||||
import java.awt.*;
|
|
||||||
|
|
||||||
import java.io.ByteArrayInputStream;
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileNotFoundException;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.InputStream;
|
|
||||||
import java.io.InputStreamReader;
|
|
||||||
import java.io.ObjectInput;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutput;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import java.net.URI;
|
|
||||||
import java.net.URLConnection;
|
|
||||||
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.awt.*;
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.net.URLConnection;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* General utilities
|
* General utilities
|
||||||
@ -60,36 +41,20 @@ public class Utilities {
|
|||||||
private static final Logger log = LoggerFactory.getLogger(Utilities.class);
|
private static final Logger log = LoggerFactory.getLogger(Utilities.class);
|
||||||
private static long lastTimeStamp = System.currentTimeMillis();
|
private static long lastTimeStamp = System.currentTimeMillis();
|
||||||
|
|
||||||
public static Timer setTimeout(long delay, ResultHandler handler) {
|
|
||||||
Timer timer = new Timer();
|
|
||||||
TimerTask task = new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Threading.USER_THREAD.execute(() -> handler.handleResult());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
timer.schedule(task, delay);
|
|
||||||
return timer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Timer setInterval(long delay, ResultHandler handler) {
|
|
||||||
Timer timer = new Timer();
|
|
||||||
TimerTask task = new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
Threading.USER_THREAD.execute(() -> handler.handleResult());
|
|
||||||
}
|
|
||||||
};
|
|
||||||
timer.scheduleAtFixedRate(task, delay, delay);
|
|
||||||
return timer;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static String objectToJson(Object object) {
|
public static String objectToJson(Object object) {
|
||||||
Gson gson =
|
Gson gson = new GsonBuilder()
|
||||||
new GsonBuilder().setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE).setPrettyPrinting().create();
|
.setExclusionStrategies(new AnnotationExclusionStrategy())
|
||||||
|
/*.excludeFieldsWithModifiers(Modifier.TRANSIENT)*/
|
||||||
|
/* .setFieldNamingPolicy(FieldNamingPolicy.UPPER_CAMEL_CASE)*/
|
||||||
|
.setPrettyPrinting()
|
||||||
|
.create();
|
||||||
return gson.toJson(object);
|
return gson.toJson(object);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static boolean isUnix() {
|
||||||
|
return isOSX() || isLinux() || getOSName().contains("freebsd");
|
||||||
|
}
|
||||||
|
|
||||||
public static boolean isWindows() {
|
public static boolean isWindows() {
|
||||||
return getOSName().contains("win");
|
return getOSName().contains("win");
|
||||||
}
|
}
|
||||||
@ -98,7 +63,7 @@ public class Utilities {
|
|||||||
return getOSName().contains("mac") || getOSName().contains("darwin");
|
return getOSName().contains("mac") || getOSName().contains("darwin");
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isLinux() {
|
private static boolean isLinux() {
|
||||||
return getOSName().contains("linux");
|
return getOSName().contains("linux");
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,8 +76,11 @@ public class Utilities {
|
|||||||
&& Desktop.isDesktopSupported()
|
&& Desktop.isDesktopSupported()
|
||||||
&& Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
|
&& Desktop.getDesktop().isSupported(Desktop.Action.BROWSE)) {
|
||||||
Desktop.getDesktop().browse(uri);
|
Desktop.getDesktop().browse(uri);
|
||||||
}
|
} else {
|
||||||
else {
|
// Maybe Application.HostServices works in those cases?
|
||||||
|
// HostServices hostServices = getHostServices();
|
||||||
|
// hostServices.showDocument(uri.toString());
|
||||||
|
|
||||||
// On Linux Desktop is poorly implemented.
|
// On Linux Desktop is poorly implemented.
|
||||||
// See https://stackoverflow.com/questions/18004150/desktop-api-is-not-supported-on-the-current-platform
|
// See https://stackoverflow.com/questions/18004150/desktop-api-is-not-supported-on-the-current-platform
|
||||||
if (!DesktopUtil.browse(uri))
|
if (!DesktopUtil.browse(uri))
|
||||||
@ -120,21 +88,76 @@ public class Utilities {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void openWebPage(String target) throws Exception {
|
public static void openWebPage(String target) {
|
||||||
openURI(new URI(target));
|
try {
|
||||||
|
openURI(new URI(target));
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
log.error(e.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void printSystemLoad() {
|
||||||
|
Runtime runtime = Runtime.getRuntime();
|
||||||
|
long free = runtime.freeMemory() / 1024 / 1024;
|
||||||
|
long total = runtime.totalMemory() / 1024 / 1024;
|
||||||
|
long used = total - free;
|
||||||
|
log.info("System load (nr. threads/used memory (MB)): " + Thread.activeCount() + "/" + used);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Opens links with http and _blank in default web browser instead of webView
|
||||||
|
// WebView has not feature to open link in default browser, so we use the hack recommended here:
|
||||||
|
// https://stackoverflow.com/questions/15555510/javafx-stop-opening-url-in-webview-open-in-browser-instead
|
||||||
|
public static void setupWebViewPopupHandler(WebEngine webEngine) {
|
||||||
|
webEngine.setCreatePopupHandler(
|
||||||
|
config -> {
|
||||||
|
// grab the last hyperlink that has :hover pseudoclass
|
||||||
|
Object result = webEngine
|
||||||
|
.executeScript(
|
||||||
|
"var list = document.querySelectorAll( ':hover' );"
|
||||||
|
+ "for (i=list.length-1; i>-1; i--) "
|
||||||
|
+ "{ if ( list.item(i).getAttribute('href') ) "
|
||||||
|
+ "{ list.item(i).getAttribute('href'); break; } }");
|
||||||
|
|
||||||
|
if (result instanceof String && ((String) result).contains("http")) {
|
||||||
|
openWebPage((String) result);
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return webEngine;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void openMail(String to, String subject, String body) {
|
||||||
|
try {
|
||||||
|
subject = URLEncoder.encode(subject, "UTF-8").replace("+", "%20");
|
||||||
|
body = URLEncoder.encode(body, "UTF-8").replace("+", "%20");
|
||||||
|
Desktop.getDesktop().mail(new URI("mailto:" + to + "?subject=" + subject + "&body=" + body));
|
||||||
|
} catch (IOException | URISyntaxException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void copyToClipboard(String content) {
|
||||||
|
if (content != null && content.length() > 0) {
|
||||||
|
Clipboard clipboard = Clipboard.getSystemClipboard();
|
||||||
|
ClipboardContent clipboardContent = new ClipboardContent();
|
||||||
|
clipboardContent.putString(content);
|
||||||
|
clipboard.setContent(clipboardContent);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] concatByteArrays(byte[]... arrays) {
|
public static byte[] concatByteArrays(byte[]... arrays) {
|
||||||
int totalLength = 0;
|
int totalLength = 0;
|
||||||
for (int i = 0; i < arrays.length; i++) {
|
for (byte[] array : arrays) {
|
||||||
totalLength += arrays[i].length;
|
totalLength += array.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte[] result = new byte[totalLength];
|
byte[] result = new byte[totalLength];
|
||||||
int currentIndex = 0;
|
int currentIndex = 0;
|
||||||
for (int i = 0; i < arrays.length; i++) {
|
for (byte[] array : arrays) {
|
||||||
System.arraycopy(arrays[i], 0, result, currentIndex, arrays[i].length);
|
System.arraycopy(array, 0, result, currentIndex, array.length);
|
||||||
currentIndex += arrays[i].length;
|
currentIndex += array.length;
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@ -146,7 +169,7 @@ public class Utilities {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static Object deserializeHexStringToObject(String serializedHexString) {
|
/* public static Object deserializeHexStringToObject(String serializedHexString) {
|
||||||
Object result = null;
|
Object result = null;
|
||||||
try {
|
try {
|
||||||
ByteArrayInputStream byteInputStream =
|
ByteArrayInputStream byteInputStream =
|
||||||
@ -183,7 +206,7 @@ public class Utilities {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}*/
|
||||||
|
|
||||||
public static <T> T byteArrayToObject(byte[] data) {
|
public static <T> T byteArrayToObject(byte[] data) {
|
||||||
ByteArrayInputStream bis = new ByteArrayInputStream(data);
|
ByteArrayInputStream bis = new ByteArrayInputStream(data);
|
||||||
@ -211,7 +234,7 @@ public class Utilities {
|
|||||||
return (T) result;
|
return (T) result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] objectToBytArray(Object object) {
|
public static byte[] objectToByteArray(Object object) {
|
||||||
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
ObjectOutput out = null;
|
ObjectOutput out = null;
|
||||||
byte[] result = null;
|
byte[] result = null;
|
||||||
@ -286,7 +309,7 @@ public class Utilities {
|
|||||||
*
|
*
|
||||||
* @param folder folder to empty
|
* @param folder folder to empty
|
||||||
*/
|
*/
|
||||||
public static void removeDirectory(final File folder) {
|
private static void removeDirectory(final File folder) {
|
||||||
// check if folder file is a real folder
|
// check if folder file is a real folder
|
||||||
if (folder.isDirectory()) {
|
if (folder.isDirectory()) {
|
||||||
File[] list = folder.listFiles();
|
File[] list = folder.listFiles();
|
||||||
@ -311,11 +334,23 @@ public class Utilities {
|
|||||||
connection.setConnectTimeout(10 * 1000);
|
connection.setConnectTimeout(10 * 1000);
|
||||||
connection.addRequestProperty("User-Agent", userAgent);
|
connection.addRequestProperty("User-Agent", userAgent);
|
||||||
connection.connect();
|
connection.connect();
|
||||||
try (InputStream inputStream = connection.getInputStream();) {
|
try (InputStream inputStream = connection.getInputStream()) {
|
||||||
return CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8));
|
return CharStreams.toString(new InputStreamReader(inputStream, Charsets.UTF_8));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static class AnnotationExclusionStrategy implements ExclusionStrategy {
|
||||||
|
@Override
|
||||||
|
public boolean shouldSkipField(FieldAttributes f) {
|
||||||
|
return f.getAnnotation(JsonExclude.class) != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldSkipClass(Class<?> clazz) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
116
core/pom.xml
116
core/pom.xml
@ -38,50 +38,46 @@
|
|||||||
</build>
|
</build>
|
||||||
|
|
||||||
<dependencies>
|
<dependencies>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.bitsquare</groupId>
|
||||||
|
<artifactId>common</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>io.bitsquare</groupId>
|
||||||
|
<artifactId>network</artifactId>
|
||||||
|
<version>${project.parent.version}</version>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.msopentech.thali</groupId>
|
||||||
|
<artifactId>universal</artifactId>
|
||||||
|
<version>0.0.3-SNAPSHOT</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-simple</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>com.msopentech.thali</groupId>
|
||||||
|
<artifactId>java</artifactId>
|
||||||
|
<version>0.0.3-SNAPSHOT</version>
|
||||||
|
<exclusions>
|
||||||
|
<exclusion>
|
||||||
|
<groupId>org.slf4j</groupId>
|
||||||
|
<artifactId>slf4j-simple</artifactId>
|
||||||
|
</exclusion>
|
||||||
|
</exclusions>
|
||||||
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>org.bitcoinj</groupId>
|
<groupId>org.bitcoinj</groupId>
|
||||||
<artifactId>bitcoinj-core</artifactId>
|
<artifactId>bitcoinj-core</artifactId>
|
||||||
<version>0.13.d13665c-SNAPSHOT</version>
|
<version>0.13.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>net.tomp2p</groupId>
|
|
||||||
<artifactId>tomp2p-all</artifactId>
|
|
||||||
<version>5.0-Beta7</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.bouncycastle</groupId>
|
|
||||||
<artifactId>bcprov-jdk15on</artifactId>
|
|
||||||
<version>1.52</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.inject</groupId>
|
|
||||||
<artifactId>guice</artifactId>
|
|
||||||
<version>3.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.guava</groupId>
|
|
||||||
<artifactId>guava</artifactId>
|
|
||||||
<version>16.0.1</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>com.google.code.gson</groupId>
|
|
||||||
<artifactId>gson</artifactId>
|
|
||||||
<version>2.2.4</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<dependency>
|
|
||||||
<groupId>io.reactivex</groupId>
|
|
||||||
<artifactId>rxjava</artifactId>
|
|
||||||
<version>1.0.0</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.springframework</groupId>
|
|
||||||
<artifactId>spring-core</artifactId>
|
|
||||||
<version>4.1.1.RELEASE</version>
|
|
||||||
</dependency>
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>net.sf.jopt-simple</groupId>
|
<groupId>net.sf.jopt-simple</groupId>
|
||||||
<artifactId>jopt-simple</artifactId>
|
<artifactId>jopt-simple</artifactId>
|
||||||
@ -93,27 +89,6 @@
|
|||||||
<artifactId>qrgen</artifactId>
|
<artifactId>qrgen</artifactId>
|
||||||
<version>1.3</version>
|
<version>1.3</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
<!-- <dependency>
|
|
||||||
<groupId>com.google.code.findbugs</groupId>
|
|
||||||
<artifactId>jsr305</artifactId>
|
|
||||||
<version>2.0.1</version>
|
|
||||||
</dependency>-->
|
|
||||||
<!-- <dependency>
|
|
||||||
<groupId>net.jcip</groupId>
|
|
||||||
<artifactId>jcip-annotations</artifactId>
|
|
||||||
<version>1.0</version>
|
|
||||||
</dependency>-->
|
|
||||||
<dependency>
|
|
||||||
<groupId>org.jetbrains</groupId>
|
|
||||||
<artifactId>annotations</artifactId>
|
|
||||||
<version>13.0</version>
|
|
||||||
</dependency>
|
|
||||||
|
|
||||||
<!-- <dependency>
|
|
||||||
<groupId>org.fxmisc.easybind</groupId>
|
|
||||||
<artifactId>easybind</artifactId>
|
|
||||||
<version>1.0.2</version>
|
|
||||||
</dependency>-->
|
|
||||||
|
|
||||||
<dependency>
|
<dependency>
|
||||||
<groupId>com.codahale.metrics</groupId>
|
<groupId>com.codahale.metrics</groupId>
|
||||||
@ -121,17 +96,16 @@
|
|||||||
<version>3.0.2</version>
|
<version>3.0.2</version>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
|
||||||
<dependency>
|
<!-- <dependency>
|
||||||
<groupId>ch.qos.logback</groupId>
|
<groupId>com.google.code.findbugs</groupId>
|
||||||
<artifactId>logback-core</artifactId>
|
<artifactId>jsr305</artifactId>
|
||||||
<version>1.1.2</version>
|
<version>2.0.1</version>
|
||||||
</dependency>
|
</dependency>-->
|
||||||
<dependency>
|
<!-- <dependency>
|
||||||
<groupId>ch.qos.logback</groupId>
|
<groupId>net.jcip</groupId>
|
||||||
<artifactId>logback-classic</artifactId>
|
<artifactId>jcip-annotations</artifactId>
|
||||||
<version>1.1.2</version>
|
<version>1.0</version>
|
||||||
</dependency>
|
</dependency>-->
|
||||||
|
|
||||||
|
|
||||||
</dependencies>
|
</dependencies>
|
||||||
</project>
|
</project>
|
||||||
|
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare;
|
|
||||||
|
|
||||||
import com.google.inject.AbstractModule;
|
|
||||||
import com.google.inject.Injector;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import org.springframework.core.env.Environment;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
|
||||||
|
|
||||||
public abstract class BitsquareModule extends AbstractModule {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(BitsquareModule.class);
|
|
||||||
protected final Environment env;
|
|
||||||
|
|
||||||
private final List<BitsquareModule> modules = new ArrayList<>();
|
|
||||||
|
|
||||||
protected BitsquareModule(Environment env) {
|
|
||||||
checkNotNull(env, "Environment must not be null");
|
|
||||||
this.env = env;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void install(BitsquareModule module) {
|
|
||||||
super.install(module);
|
|
||||||
modules.add(module);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Close any instances this module is responsible for and recursively close any
|
|
||||||
* sub-modules installed via {@link #install(BitsquareModule)}. This method
|
|
||||||
* must be called manually, e.g. at the end of a main() method or in the stop() method
|
|
||||||
* of a JavaFX Application; alternatively it may be registered as a JVM shutdown hook.
|
|
||||||
*
|
|
||||||
* @param injector the Injector originally initialized with this module
|
|
||||||
* @see #doClose(com.google.inject.Injector)
|
|
||||||
*/
|
|
||||||
public final void close(Injector injector) {
|
|
||||||
modules.forEach(module -> module.close(injector));
|
|
||||||
doClose(injector);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Actually perform closing of any instances this module is responsible for. Called by
|
|
||||||
* {@link #close(Injector)}.
|
|
||||||
*
|
|
||||||
* @param injector the Injector originally initialized with this module
|
|
||||||
*/
|
|
||||||
protected void doClose(Injector injector) {
|
|
||||||
}
|
|
||||||
}
|
|
@ -21,24 +21,13 @@ import io.bitsquare.BitsquareException;
|
|||||||
import io.bitsquare.btc.BitcoinNetwork;
|
import io.bitsquare.btc.BitcoinNetwork;
|
||||||
import io.bitsquare.btc.UserAgent;
|
import io.bitsquare.btc.UserAgent;
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
import io.bitsquare.crypto.KeyStorage;
|
import io.bitsquare.common.crypto.KeyStorage;
|
||||||
import io.bitsquare.p2p.tomp2p.TomP2PModule;
|
import io.bitsquare.common.util.Utilities;
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.util.Utilities;
|
|
||||||
import io.bitsquare.util.spring.JOptCommandLinePropertySource;
|
import io.bitsquare.util.spring.JOptCommandLinePropertySource;
|
||||||
|
import joptsimple.OptionSet;
|
||||||
import java.io.File;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.nio.file.Paths;
|
|
||||||
|
|
||||||
import java.util.Properties;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import joptsimple.OptionSet;
|
|
||||||
import org.springframework.core.env.MutablePropertySources;
|
import org.springframework.core.env.MutablePropertySources;
|
||||||
import org.springframework.core.env.PropertiesPropertySource;
|
import org.springframework.core.env.PropertiesPropertySource;
|
||||||
import org.springframework.core.env.PropertySource;
|
import org.springframework.core.env.PropertySource;
|
||||||
@ -48,6 +37,12 @@ import org.springframework.core.io.Resource;
|
|||||||
import org.springframework.core.io.ResourceLoader;
|
import org.springframework.core.io.ResourceLoader;
|
||||||
import org.springframework.core.io.support.ResourcePropertySource;
|
import org.springframework.core.io.support.ResourcePropertySource;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.nio.file.Paths;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
public class BitsquareEnvironment extends StandardEnvironment {
|
public class BitsquareEnvironment extends StandardEnvironment {
|
||||||
@ -79,11 +74,11 @@ public class BitsquareEnvironment extends StandardEnvironment {
|
|||||||
private final String userDataDir;
|
private final String userDataDir;
|
||||||
private final String appDataDir;
|
private final String appDataDir;
|
||||||
private final String btcNetworkDir;
|
private final String btcNetworkDir;
|
||||||
private final String bootstrapNodePort;
|
|
||||||
private BitcoinNetwork bitcoinNetwork;
|
private BitcoinNetwork bitcoinNetwork;
|
||||||
|
|
||||||
public BitsquareEnvironment(OptionSet options) {
|
public BitsquareEnvironment(OptionSet options) {
|
||||||
this(new JOptCommandLinePropertySource(BITSQUARE_COMMANDLINE_PROPERTY_SOURCE_NAME, checkNotNull(options)));
|
this(new JOptCommandLinePropertySource(BITSQUARE_COMMANDLINE_PROPERTY_SOURCE_NAME, checkNotNull(
|
||||||
|
options)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public BitcoinNetwork getBitcoinNetwork() {
|
public BitcoinNetwork getBitcoinNetwork() {
|
||||||
@ -99,8 +94,7 @@ public class BitsquareEnvironment extends StandardEnvironment {
|
|||||||
Object propertiesObject = appDirProperties().getSource();
|
Object propertiesObject = appDirProperties().getSource();
|
||||||
if (propertiesObject instanceof Properties) {
|
if (propertiesObject instanceof Properties) {
|
||||||
properties = (Properties) propertiesObject;
|
properties = (Properties) propertiesObject;
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
log.warn("propertiesObject not instance of Properties");
|
log.warn("propertiesObject not instance of Properties");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -129,9 +123,6 @@ public class BitsquareEnvironment extends StandardEnvironment {
|
|||||||
(String) commandLineProperties.getProperty(APP_DATA_DIR_KEY) :
|
(String) commandLineProperties.getProperty(APP_DATA_DIR_KEY) :
|
||||||
appDataDir(userDataDir, appName);
|
appDataDir(userDataDir, appName);
|
||||||
|
|
||||||
bootstrapNodePort = commandLineProperties.containsProperty(TomP2PModule.BOOTSTRAP_NODE_PORT_KEY) ?
|
|
||||||
(String) commandLineProperties.getProperty(TomP2PModule.BOOTSTRAP_NODE_PORT_KEY) : "-1";
|
|
||||||
|
|
||||||
MutablePropertySources propertySources = this.getPropertySources();
|
MutablePropertySources propertySources = this.getPropertySources();
|
||||||
propertySources.addFirst(commandLineProperties);
|
propertySources.addFirst(commandLineProperties);
|
||||||
try {
|
try {
|
||||||
@ -166,7 +157,7 @@ public class BitsquareEnvironment extends StandardEnvironment {
|
|||||||
return new ResourcePropertySource(BITSQUARE_APP_DIR_PROPERTY_SOURCE_NAME, resource);
|
return new ResourcePropertySource(BITSQUARE_APP_DIR_PROPERTY_SOURCE_NAME, resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertySource<?> homeDirProperties() throws Exception {
|
private PropertySource<?> homeDirProperties() throws Exception {
|
||||||
String location = String.format("file:%s/.bitsquare/bitsquare.properties", getProperty("user.home"));
|
String location = String.format("file:%s/.bitsquare/bitsquare.properties", getProperty("user.home"));
|
||||||
Resource resource = resourceLoader.getResource(location);
|
Resource resource = resourceLoader.getResource(location);
|
||||||
|
|
||||||
@ -176,12 +167,12 @@ public class BitsquareEnvironment extends StandardEnvironment {
|
|||||||
return new ResourcePropertySource(BITSQUARE_HOME_DIR_PROPERTY_SOURCE_NAME, resource);
|
return new ResourcePropertySource(BITSQUARE_HOME_DIR_PROPERTY_SOURCE_NAME, resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
PropertySource<?> classpathProperties() throws Exception {
|
private PropertySource<?> classpathProperties() throws Exception {
|
||||||
Resource resource = resourceLoader.getResource("classpath:bitsquare.properties");
|
Resource resource = resourceLoader.getResource("classpath:bitsquare.properties");
|
||||||
return new ResourcePropertySource(BITSQUARE_CLASSPATH_PROPERTY_SOURCE_NAME, resource);
|
return new ResourcePropertySource(BITSQUARE_CLASSPATH_PROPERTY_SOURCE_NAME, resource);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected PropertySource<?> defaultProperties() {
|
private PropertySource<?> defaultProperties() {
|
||||||
return new PropertiesPropertySource(BITSQUARE_DEFAULT_PROPERTY_SOURCE_NAME, new Properties() {
|
return new PropertiesPropertySource(BITSQUARE_DEFAULT_PROPERTY_SOURCE_NAME, new Properties() {
|
||||||
private static final long serialVersionUID = -8478089705207326165L;
|
private static final long serialVersionUID = -8478089705207326165L;
|
||||||
|
|
||||||
@ -200,8 +191,7 @@ public class BitsquareEnvironment extends StandardEnvironment {
|
|||||||
|
|
||||||
setProperty(Storage.DIR_KEY, Paths.get(btcNetworkDir, "db").toString());
|
setProperty(Storage.DIR_KEY, Paths.get(btcNetworkDir, "db").toString());
|
||||||
setProperty(KeyStorage.DIR_KEY, Paths.get(btcNetworkDir, "keys").toString());
|
setProperty(KeyStorage.DIR_KEY, Paths.get(btcNetworkDir, "keys").toString());
|
||||||
|
setProperty(ProgramArguments.TOR_DIR, Paths.get(btcNetworkDir, "tor").toString());
|
||||||
setProperty(TomP2PModule.BOOTSTRAP_NODE_PORT_KEY, bootstrapNodePort);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -22,12 +22,13 @@ import joptsimple.OptionParser;
|
|||||||
import joptsimple.OptionSet;
|
import joptsimple.OptionSet;
|
||||||
import org.springframework.util.StringUtils;
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
import static java.lang.String.*;
|
import static java.lang.String.format;
|
||||||
|
import static java.lang.String.join;
|
||||||
|
|
||||||
public abstract class BitsquareExecutable {
|
public abstract class BitsquareExecutable {
|
||||||
public static final int EXIT_SUCCESS = 0;
|
private static final int EXIT_SUCCESS = 0;
|
||||||
public static final int EXIT_FAILURE = 1;
|
public static final int EXIT_FAILURE = 1;
|
||||||
public static final String HELP_KEY = "help";
|
private static final String HELP_KEY = "help";
|
||||||
|
|
||||||
public void execute(String[] args) throws Exception {
|
public void execute(String[] args) throws Exception {
|
||||||
OptionParser parser = new OptionParser();
|
OptionParser parser = new OptionParser();
|
||||||
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.app;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class Version {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(Version.class);
|
|
||||||
|
|
||||||
public static final int MAJOR_VERSION = 0;
|
|
||||||
public static final int MINOR_VERSION = 3;
|
|
||||||
public static final int PATCH_VERSION = 1;
|
|
||||||
|
|
||||||
public static final String VERSION = MAJOR_VERSION + "." + MINOR_VERSION + "." + PATCH_VERSION;
|
|
||||||
|
|
||||||
// If objects are used for both network and database the network version is applied.
|
|
||||||
public static final long NETWORK_PROTOCOL_VERSION = 1;
|
|
||||||
public static final long LOCAL_DB_VERSION = 1;
|
|
||||||
}
|
|
@ -1,130 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.arbitration;
|
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
|
||||||
import io.bitsquare.crypto.Util;
|
|
||||||
import io.bitsquare.locale.LanguageUtil;
|
|
||||||
import io.bitsquare.storage.Storage;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
import org.bitcoinj.core.Utils;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.MapChangeListener;
|
|
||||||
import javafx.collections.ObservableMap;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class ArbitrationRepository implements Serializable {
|
|
||||||
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
|
||||||
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
|
|
||||||
transient private static final Logger log = LoggerFactory.getLogger(ArbitrationRepository.class);
|
|
||||||
|
|
||||||
transient private final ArbitratorService arbitratorService;
|
|
||||||
transient private final Arbitrator defaultArbitrator;
|
|
||||||
transient private final ObservableMap<String, Arbitrator> arbitratorsObservableMap = FXCollections.observableHashMap();
|
|
||||||
transient private boolean allArbitratorsSynced;
|
|
||||||
|
|
||||||
// Persisted fields
|
|
||||||
private final Map<String, Arbitrator> arbitratorsMap = new HashMap<>();
|
|
||||||
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public ArbitrationRepository(Storage<ArbitrationRepository> storage,
|
|
||||||
Storage<Arbitrator> arbitratorStorage,
|
|
||||||
ArbitratorService arbitratorService) throws InvalidKeySpecException, NoSuchAlgorithmException {
|
|
||||||
this.arbitratorService = arbitratorService;
|
|
||||||
|
|
||||||
byte[] walletPubKey = Utils.HEX.decode("03a418bf0cb60a35ce217c7f80a2db08a4f5efbe56a0e7602fbc392dea6b63f840");
|
|
||||||
PublicKey p2pSigPubKey = null;
|
|
||||||
p2pSigPubKey = Util.decodeDSAPubKeyHex
|
|
||||||
("308201b83082012c06072a8648ce3804013082011f02818100fd7f53811d75122952df4a9c2eece4e7f611b7523cef4400c31e3f80b6512669455d402251fb593d8d58fabfc5f5ba30f6cb9b556cd7813b801d346ff26660b76b9950a5a49f9fe8047b1022c24fbba9d7feb7c61bf83b57e7c6a8a6150f04fb83f6d3c51ec3023554135a169132f675f3ae2b61d72aeff22203199dd14801c70215009760508f15230bccb292b982a2eb840bf0581cf502818100f7e1a085d69b3ddecbbcab5c36b857b97994afbbfa3aea82f9574c0b3d0782675159578ebad4594fe67107108180b449167123e84c281613b7cf09328cc8a6e13c167a8b547c8d28e0a3ae1e2bb3a675916ea37f0bfa213562f1fb627a01243bcca4f1bea8519089a883dfe15ae59f06928b665e807b552564014c3bfecf492a0381850002818100db47d4cf76e9bfcc0ba1e98c21c19ba45d1440fa2fec732f664dc8fd63e98877e648aac6db8d1035cd640fe5ff2e0030c2f8694ed124e81bd42c5446a1ce5288d5c8b4073d1cd890fe61ee4527f4e3184279f394cb9c2a4e7924cb2e82320a846cc140304eac6d41d4eaebc4d69b92725715497a82890be9f49d348fda20b095");
|
|
||||||
this.defaultArbitrator = new Arbitrator(arbitratorStorage,
|
|
||||||
"default-524f-46c0-b96e-de5a11d3475d",
|
|
||||||
walletPubKey,
|
|
||||||
p2pSigPubKey,
|
|
||||||
"Mr. Default",
|
|
||||||
new Reputation(),
|
|
||||||
Arbitrator.ID_TYPE.REAL_LIFE_ID,
|
|
||||||
Arrays.asList(LanguageUtil.getDefaultLanguageLocaleAsCode()),
|
|
||||||
Coin.parseCoin("0.1"),
|
|
||||||
Arrays.asList(Arbitrator.METHOD.TLS_NOTARY),
|
|
||||||
Arrays.asList(Arbitrator.ID_VERIFICATION.PASSPORT),
|
|
||||||
"https://bitsquare.io",
|
|
||||||
"Bla bla...");
|
|
||||||
|
|
||||||
ArbitrationRepository persisted = storage.initAndGetPersisted(this);
|
|
||||||
if (persisted != null) {
|
|
||||||
arbitratorsMap.putAll(persisted.getArbitratorsMap());
|
|
||||||
}
|
|
||||||
arbitratorsMap.put(defaultArbitrator.getId(), defaultArbitrator);
|
|
||||||
|
|
||||||
arbitratorsObservableMap.putAll(arbitratorsMap);
|
|
||||||
arbitratorsObservableMap.addListener((MapChangeListener<String, Arbitrator>) change -> storage.queueUpForSave());
|
|
||||||
allArbitratorsSynced = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Is called when all services are ready
|
|
||||||
public void loadAllArbitrators() {
|
|
||||||
log.debug("loadAllArbitrators");
|
|
||||||
arbitratorService.loadAllArbitrators((Map<String, Arbitrator> arbitratorsMap) -> {
|
|
||||||
log.debug("Arbitrators successful loaded.");
|
|
||||||
log.debug("arbitratorsMap.size()=" + arbitratorsMap.size());
|
|
||||||
ArbitrationRepository.this.arbitratorsMap.clear();
|
|
||||||
ArbitrationRepository.this.arbitratorsMap.put(defaultArbitrator.getId(), defaultArbitrator);
|
|
||||||
ArbitrationRepository.this.arbitratorsMap.putAll(arbitratorsMap);
|
|
||||||
ArbitrationRepository.this.arbitratorsObservableMap.clear();
|
|
||||||
ArbitrationRepository.this.arbitratorsObservableMap.putAll(ArbitrationRepository.this.arbitratorsMap);
|
|
||||||
allArbitratorsSynced = true;
|
|
||||||
},
|
|
||||||
(log::error));
|
|
||||||
}
|
|
||||||
|
|
||||||
public Map<String, Arbitrator> getArbitratorsMap() {
|
|
||||||
return arbitratorsMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObservableMap<String, Arbitrator> getArbitratorsObservableMap() {
|
|
||||||
return arbitratorsObservableMap;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean areAllArbitratorsSynced() {
|
|
||||||
return allArbitratorsSynced;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public Arbitrator getDefaultArbitrator() {
|
|
||||||
return defaultArbitrator;
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,165 +18,47 @@
|
|||||||
package io.bitsquare.arbitration;
|
package io.bitsquare.arbitration;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.common.crypto.PubKeyRing;
|
||||||
|
import io.bitsquare.p2p.Address;
|
||||||
import org.bitcoinj.core.Coin;
|
import io.bitsquare.p2p.storage.data.PubKeyProtectedExpirablePayload;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
public class Arbitrator implements Serializable {
|
public final class Arbitrator implements PubKeyProtectedExpirablePayload {
|
||||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
// That object is sent over the wire, so we need to take care of version compatibility.
|
||||||
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
|
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
|
||||||
|
|
||||||
|
public static final long TTL = 10 * 24 * 60 * 60 * 1000; // 10 days
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Enums
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public enum ID_TYPE {
|
|
||||||
REAL_LIFE_ID,
|
|
||||||
NICKNAME,
|
|
||||||
COMPANY
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum METHOD {
|
|
||||||
TLS_NOTARY,
|
|
||||||
SKYPE_SCREEN_SHARING,
|
|
||||||
SMART_PHONE_VIDEO_CHAT,
|
|
||||||
REQUIRE_REAL_ID,
|
|
||||||
BANK_STATEMENT,
|
|
||||||
OTHER
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ID_VERIFICATION {
|
|
||||||
PASSPORT,
|
|
||||||
GOV_ID,
|
|
||||||
UTILITY_BILLS,
|
|
||||||
FACEBOOK,
|
|
||||||
GOOGLE_PLUS,
|
|
||||||
TWITTER,
|
|
||||||
PGP,
|
|
||||||
BTC_OTC,
|
|
||||||
OTHER
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
final transient private Storage<Arbitrator> storage;
|
|
||||||
|
|
||||||
// Persisted fields
|
// Persisted fields
|
||||||
private final String id;
|
private final byte[] btcPubKey;
|
||||||
private final byte[] pubKey;
|
private final PubKeyRing pubKeyRing;
|
||||||
private final PublicKey p2pSigPubKey;
|
private final Address arbitratorAddress;
|
||||||
private final String name;
|
private final List<String> languageCodes;
|
||||||
private final Reputation reputation;
|
private final String btcAddress;
|
||||||
|
private final long registrationDate;
|
||||||
|
private final String registrationSignature;
|
||||||
|
private final byte[] registrationPubKey;
|
||||||
|
|
||||||
// Mutable
|
public Arbitrator(Address arbitratorAddress,
|
||||||
private ID_TYPE idType;
|
byte[] btcPubKey,
|
||||||
private List<String> languageCodes;
|
String btcAddress,
|
||||||
private Coin fee;
|
PubKeyRing pubKeyRing,
|
||||||
private List<METHOD> arbitrationMethods;
|
|
||||||
private List<ID_VERIFICATION> idVerifications;
|
|
||||||
private String webUrl;
|
|
||||||
private String description;
|
|
||||||
|
|
||||||
public Arbitrator(Storage<Arbitrator> storage,
|
|
||||||
String id,
|
|
||||||
byte[] pubKey,
|
|
||||||
PublicKey p2pSigPubKey,
|
|
||||||
String name,
|
|
||||||
Reputation reputation,
|
|
||||||
ID_TYPE idType,
|
|
||||||
List<String> languageCodes,
|
List<String> languageCodes,
|
||||||
Coin fee,
|
Date registrationDate,
|
||||||
List<METHOD> arbitrationMethods,
|
byte[] registrationPubKey,
|
||||||
List<ID_VERIFICATION> idVerifications,
|
String registrationSignature) {
|
||||||
String webUrl,
|
this.arbitratorAddress = arbitratorAddress;
|
||||||
String description) {
|
this.btcPubKey = btcPubKey;
|
||||||
this.storage = storage;
|
this.btcAddress = btcAddress;
|
||||||
this.id = id;
|
this.pubKeyRing = pubKeyRing;
|
||||||
this.pubKey = pubKey;
|
|
||||||
this.p2pSigPubKey = p2pSigPubKey;
|
|
||||||
this.name = name;
|
|
||||||
this.reputation = reputation;
|
|
||||||
this.idType = idType;
|
|
||||||
this.languageCodes = languageCodes;
|
this.languageCodes = languageCodes;
|
||||||
this.fee = fee;
|
this.registrationDate = registrationDate.getTime();
|
||||||
this.arbitrationMethods = arbitrationMethods;
|
this.registrationPubKey = registrationPubKey;
|
||||||
this.idVerifications = idVerifications;
|
this.registrationSignature = registrationSignature;
|
||||||
this.webUrl = webUrl;
|
|
||||||
this.description = description;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void save() {
|
|
||||||
storage.queueUpForSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
if (id != null) {
|
|
||||||
return Objects.hashCode(id);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (!(obj instanceof Arbitrator)) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (obj == this) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
Arbitrator other = (Arbitrator) obj;
|
|
||||||
return id != null && id.equals(other.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Setters
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public void setDescription(String description) {
|
|
||||||
this.description = description;
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIdType(ID_TYPE idType) {
|
|
||||||
this.idType = idType;
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setLanguageCodes(List<String> languageCodes) {
|
|
||||||
this.languageCodes = languageCodes;
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFee(Coin fee) {
|
|
||||||
this.fee = fee;
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setArbitrationMethods(List<METHOD> arbitrationMethods) {
|
|
||||||
this.arbitrationMethods = arbitrationMethods;
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setIdVerifications(List<ID_VERIFICATION> idVerifications) {
|
|
||||||
this.idVerifications = idVerifications;
|
|
||||||
save();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setWebUrl(String webUrl) {
|
|
||||||
this.webUrl = webUrl;
|
|
||||||
save();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -184,51 +66,88 @@ public class Arbitrator implements Serializable {
|
|||||||
// Getters
|
// Getters
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public String getId() {
|
@Override
|
||||||
return id;
|
public long getTTL() {
|
||||||
|
return TTL;
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getPubKey() {
|
@Override
|
||||||
return pubKey;
|
public PublicKey getPubKey() {
|
||||||
|
return pubKeyRing.getStorageSignaturePubKey();
|
||||||
}
|
}
|
||||||
|
|
||||||
public PublicKey getP2pSigPubKey() {
|
public byte[] getBtcPubKey() {
|
||||||
return p2pSigPubKey;
|
return btcPubKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getName() {
|
public PubKeyRing getPubKeyRing() {
|
||||||
return name;
|
return pubKeyRing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ID_TYPE getIdType() {
|
public Address getArbitratorAddress() {
|
||||||
return idType;
|
return arbitratorAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Date getRegistrationDate() {
|
||||||
|
return new Date(registrationDate);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBtcAddress() {
|
||||||
|
return btcAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getLanguageCodes() {
|
public List<String> getLanguageCodes() {
|
||||||
return languageCodes;
|
return languageCodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Reputation getReputation() {
|
public String getRegistrationSignature() {
|
||||||
return reputation;
|
return registrationSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Coin getFee() {
|
public byte[] getRegistrationPubKey() {
|
||||||
return fee;
|
return registrationPubKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<METHOD> getArbitrationMethods() {
|
@Override
|
||||||
return arbitrationMethods;
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof Arbitrator)) return false;
|
||||||
|
|
||||||
|
Arbitrator that = (Arbitrator) o;
|
||||||
|
|
||||||
|
if (registrationDate != that.registrationDate) return false;
|
||||||
|
if (!Arrays.equals(btcPubKey, that.btcPubKey)) return false;
|
||||||
|
if (pubKeyRing != null ? !pubKeyRing.equals(that.pubKeyRing) : that.pubKeyRing != null) return false;
|
||||||
|
if (arbitratorAddress != null ? !arbitratorAddress.equals(that.arbitratorAddress) : that.arbitratorAddress != null)
|
||||||
|
return false;
|
||||||
|
if (languageCodes != null ? !languageCodes.equals(that.languageCodes) : that.languageCodes != null)
|
||||||
|
return false;
|
||||||
|
if (btcAddress != null ? !btcAddress.equals(that.btcAddress) : that.btcAddress != null) return false;
|
||||||
|
if (registrationSignature != null ? !registrationSignature.equals(that.registrationSignature) : that.registrationSignature != null)
|
||||||
|
return false;
|
||||||
|
return Arrays.equals(registrationPubKey, that.registrationPubKey);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ID_VERIFICATION> getIdVerifications() {
|
@Override
|
||||||
return idVerifications;
|
public int hashCode() {
|
||||||
|
int result = btcPubKey != null ? Arrays.hashCode(btcPubKey) : 0;
|
||||||
|
result = 31 * result + (pubKeyRing != null ? pubKeyRing.hashCode() : 0);
|
||||||
|
result = 31 * result + (arbitratorAddress != null ? arbitratorAddress.hashCode() : 0);
|
||||||
|
result = 31 * result + (languageCodes != null ? languageCodes.hashCode() : 0);
|
||||||
|
result = 31 * result + (btcAddress != null ? btcAddress.hashCode() : 0);
|
||||||
|
result = 31 * result + (int) (registrationDate ^ (registrationDate >>> 32));
|
||||||
|
result = 31 * result + (registrationSignature != null ? registrationSignature.hashCode() : 0);
|
||||||
|
result = 31 * result + (registrationPubKey != null ? Arrays.hashCode(registrationPubKey) : 0);
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getWebUrl() {
|
@Override
|
||||||
return webUrl;
|
public String toString() {
|
||||||
}
|
return "Arbitrator{" +
|
||||||
|
"arbitratorAddress='" + arbitratorAddress + '\'' +
|
||||||
public String getDescription() {
|
", languageCodes=" + languageCodes +
|
||||||
return description;
|
", btcAddress='" + btcAddress + '\'' +
|
||||||
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,25 +17,28 @@
|
|||||||
|
|
||||||
package io.bitsquare.arbitration;
|
package io.bitsquare.arbitration;
|
||||||
|
|
||||||
import io.bitsquare.BitsquareModule;
|
|
||||||
|
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
import com.google.inject.name.Names;
|
||||||
|
import io.bitsquare.app.AppModule;
|
||||||
|
import io.bitsquare.app.ProgramArguments;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
public abstract class ArbitratorModule extends BitsquareModule {
|
public class ArbitratorModule extends AppModule {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ArbitratorModule.class);
|
||||||
|
|
||||||
protected ArbitratorModule(Environment env) {
|
public ArbitratorModule(Environment env) {
|
||||||
super(env);
|
super(env);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected final void configure() {
|
protected final void configure() {
|
||||||
bind(ArbitrationRepository.class).in(Singleton.class);
|
bind(ArbitratorManager.class).in(Singleton.class);
|
||||||
|
bind(DisputeManager.class).in(Singleton.class);
|
||||||
|
bind(ArbitratorService.class).in(Singleton.class);
|
||||||
|
|
||||||
doConfigure();
|
Boolean devTest = env.getProperty(ProgramArguments.DEV_TEST, boolean.class, false);
|
||||||
}
|
bind(boolean.class).annotatedWith(Names.named(ProgramArguments.DEV_TEST)).toInstance(devTest);
|
||||||
|
|
||||||
protected void doConfigure() {
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,33 +17,75 @@
|
|||||||
|
|
||||||
package io.bitsquare.arbitration;
|
package io.bitsquare.arbitration;
|
||||||
|
|
||||||
|
|
||||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||||
import io.bitsquare.common.handlers.ResultHandler;
|
import io.bitsquare.common.handlers.ResultHandler;
|
||||||
import io.bitsquare.p2p.DHTService;
|
import io.bitsquare.p2p.Address;
|
||||||
|
import io.bitsquare.p2p.P2PService;
|
||||||
|
import io.bitsquare.p2p.storage.HashSetChangedListener;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.inject.Inject;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public interface ArbitratorService extends DHTService {
|
/**
|
||||||
|
* Used to store arbitrators profile and load map of arbitrators
|
||||||
|
*/
|
||||||
|
public class ArbitratorService {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ArbitratorService.class);
|
||||||
|
|
||||||
void addListener(Listener listener);
|
private P2PService p2PService;
|
||||||
|
|
||||||
void removeListener(Listener listener);
|
|
||||||
|
|
||||||
void addArbitrator(Arbitrator arbitrator, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler);
|
|
||||||
|
|
||||||
void loadAllArbitrators(ArbitratorMapResultHandler resultHandler, ErrorMessageHandler errorMessageHandler);
|
|
||||||
|
|
||||||
interface Listener {
|
|
||||||
void onArbitratorAdded(Arbitrator arbitrator);
|
|
||||||
|
|
||||||
void onAllArbitratorsLoaded(Map<String, Arbitrator> arbitratorsMap);
|
|
||||||
|
|
||||||
void onArbitratorRemoved(Arbitrator arbitrator);
|
|
||||||
}
|
|
||||||
|
|
||||||
interface ArbitratorMapResultHandler {
|
interface ArbitratorMapResultHandler {
|
||||||
void handleResult(Map<String, Arbitrator> arbitratorsMap);
|
void handleResult(Map<String, Arbitrator> arbitratorsMap);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
public ArbitratorService(P2PService p2PService) {
|
||||||
|
this.p2PService = p2PService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHashSetChangedListener(HashSetChangedListener hashSetChangedListener) {
|
||||||
|
p2PService.addHashSetChangedListener(hashSetChangedListener);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addArbitrator(Arbitrator arbitrator, final ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
log.debug("addArbitrator arbitrator.hashCode() " + arbitrator.hashCode());
|
||||||
|
boolean result = p2PService.addData(arbitrator);
|
||||||
|
if (result) {
|
||||||
|
log.trace("Add arbitrator to network was successful. Arbitrator = " + arbitrator);
|
||||||
|
resultHandler.handleResult();
|
||||||
|
} else {
|
||||||
|
errorMessageHandler.handleErrorMessage("Add arbitrator failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void removeArbitrator(Arbitrator arbitrator, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
||||||
|
log.debug("removeArbitrator arbitrator.hashCode() " + arbitrator.hashCode());
|
||||||
|
if (p2PService.removeData(arbitrator)) {
|
||||||
|
log.trace("Remove arbitrator from network was successful. Arbitrator = " + arbitrator);
|
||||||
|
resultHandler.handleResult();
|
||||||
|
} else {
|
||||||
|
errorMessageHandler.handleErrorMessage("Remove arbitrator failed");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
P2PService getP2PService() {
|
||||||
|
return p2PService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Address, Arbitrator> getArbitrators() {
|
||||||
|
final Map<Address, Arbitrator> arbitratorsMap = p2PService.getDataMap().values().stream()
|
||||||
|
.filter(e -> e.expirablePayload instanceof Arbitrator)
|
||||||
|
.map(e -> (Arbitrator) e.expirablePayload)
|
||||||
|
.collect(Collectors.toMap(e -> e.getArbitratorAddress(), e -> e));
|
||||||
|
|
||||||
|
return arbitratorsMap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,43 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.arbitration;
|
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
//TODO still open if we use that really...
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Reputation for Arbitrators
|
|
||||||
*/
|
|
||||||
public class Reputation implements Serializable {
|
|
||||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
|
||||||
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
|
|
||||||
|
|
||||||
//TODO
|
|
||||||
public Reputation() {
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "4 positive ratings in 5 cases";
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.arbitration.tomp2p;
|
|
||||||
|
|
||||||
import io.bitsquare.arbitration.ArbitratorModule;
|
|
||||||
import io.bitsquare.arbitration.ArbitratorService;
|
|
||||||
|
|
||||||
import com.google.inject.Singleton;
|
|
||||||
|
|
||||||
import org.springframework.core.env.Environment;
|
|
||||||
|
|
||||||
public class TomP2PArbitratorModule extends ArbitratorModule {
|
|
||||||
|
|
||||||
public TomP2PArbitratorModule(Environment env) {
|
|
||||||
super(env);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doConfigure() {
|
|
||||||
bind(ArbitratorService.class).to(TomP2PArbitratorService.class).in(Singleton.class);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,173 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.arbitration.tomp2p;
|
|
||||||
|
|
||||||
import io.bitsquare.arbitration.Arbitrator;
|
|
||||||
import io.bitsquare.arbitration.ArbitratorService;
|
|
||||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
|
||||||
import io.bitsquare.common.handlers.ResultHandler;
|
|
||||||
import io.bitsquare.crypto.KeyRing;
|
|
||||||
import io.bitsquare.p2p.tomp2p.TomP2PDHTService;
|
|
||||||
import io.bitsquare.p2p.tomp2p.TomP2PNode;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.util.HashMap;
|
|
||||||
import java.util.Map;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import net.tomp2p.dht.FutureGet;
|
|
||||||
import net.tomp2p.dht.FuturePut;
|
|
||||||
import net.tomp2p.dht.FutureRemove;
|
|
||||||
import net.tomp2p.futures.BaseFuture;
|
|
||||||
import net.tomp2p.futures.BaseFutureAdapter;
|
|
||||||
import net.tomp2p.peers.Number160;
|
|
||||||
import net.tomp2p.storage.Data;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
|
|
||||||
public class TomP2PArbitratorService extends TomP2PDHTService implements ArbitratorService {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(TomP2PArbitratorService.class);
|
|
||||||
|
|
||||||
private static final Number160 LOCATION_KEY = Number160.createHash("ArbitratorService");
|
|
||||||
|
|
||||||
private final CopyOnWriteArrayList<Listener> listeners = new CopyOnWriteArrayList<>();
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public TomP2PArbitratorService(TomP2PNode tomP2PNode, KeyRing keyRing) {
|
|
||||||
super(tomP2PNode, keyRing);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addArbitrator(Arbitrator arbitrator, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
try {
|
|
||||||
final Data arbitratorData = new Data(arbitrator);
|
|
||||||
|
|
||||||
openRequestsUp();
|
|
||||||
FuturePut addFuture = addProtectedDataToMap(LOCATION_KEY, arbitratorData);
|
|
||||||
addFuture.addListener(new BaseFutureAdapter<BaseFuture>() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(BaseFuture future) throws Exception {
|
|
||||||
openRequestsDown();
|
|
||||||
if (future.isSuccess()) {
|
|
||||||
log.trace("Add arbitrator to DHT was successful. Stored data: [key: " + LOCATION_KEY + ", " +
|
|
||||||
"values: " + arbitratorData + "]");
|
|
||||||
Object arbitratorDataObject = arbitratorData.object();
|
|
||||||
if (arbitratorDataObject instanceof Arbitrator) {
|
|
||||||
Arbitrator result = (Arbitrator) arbitratorDataObject;
|
|
||||||
executor.execute(() -> {
|
|
||||||
resultHandler.handleResult();
|
|
||||||
listeners.stream().forEach(listener -> listener.onArbitratorAdded(result));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.error("Add arbitrator to DHT failed with reason:" + addFuture.failedReason());
|
|
||||||
errorMessageHandler.handleErrorMessage("Add arbitrator to DHT failed with reason:" + addFuture.failedReason());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (IOException e) {
|
|
||||||
openRequestsDown();
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeArbitrator(Arbitrator arbitrator) throws IOException {
|
|
||||||
final Data arbitratorData = new Data(arbitrator);
|
|
||||||
openRequestsUp();
|
|
||||||
FutureRemove removeFuture = removeProtectedDataFromMap(LOCATION_KEY, arbitratorData);
|
|
||||||
removeFuture.addListener(new BaseFutureAdapter<BaseFuture>() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(BaseFuture future) throws Exception {
|
|
||||||
openRequestsDown();
|
|
||||||
for (Data arbitratorData : removeFuture.dataMap().values()) {
|
|
||||||
try {
|
|
||||||
Object arbitratorDataObject = arbitratorData.object();
|
|
||||||
if (arbitratorDataObject instanceof Arbitrator) {
|
|
||||||
Arbitrator arbitrator = (Arbitrator) arbitratorDataObject;
|
|
||||||
executor.execute(() -> listeners.stream().forEach(listener -> listener.onArbitratorRemoved(arbitrator)));
|
|
||||||
}
|
|
||||||
} catch (ClassNotFoundException | IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// We don't test futureRemove.isSuccess() as this API does not fit well to that operation,
|
|
||||||
// it might change in future to something like foundAndRemoved and notFound
|
|
||||||
// See discussion at: https://github.com/tomp2p/TomP2P/issues/57#issuecomment-62069840
|
|
||||||
|
|
||||||
log.trace("Remove arbitrator from DHT was successful. Stored data: [key: " + LOCATION_KEY + ", " +
|
|
||||||
"values: " + arbitratorData + "]");
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public void loadAllArbitrators(ArbitratorMapResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
FutureGet futureGet = getMap(LOCATION_KEY);
|
|
||||||
futureGet.addListener(new BaseFutureAdapter<BaseFuture>() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(BaseFuture future) throws Exception {
|
|
||||||
if (future.isSuccess()) {
|
|
||||||
log.trace("Get arbitrators from DHT was successful. Stored data: [key: " + LOCATION_KEY + ", " +
|
|
||||||
"values: " + futureGet.dataMap() + "]");
|
|
||||||
|
|
||||||
final Map<String, Arbitrator> arbitratorsMap = new HashMap<>();
|
|
||||||
for (Data arbitratorData : futureGet.dataMap().values()) {
|
|
||||||
try {
|
|
||||||
Object arbitratorDataObject = arbitratorData.object();
|
|
||||||
if (arbitratorDataObject instanceof Arbitrator) {
|
|
||||||
Arbitrator arbitrator = (Arbitrator) arbitratorDataObject;
|
|
||||||
arbitratorsMap.put(arbitrator.getId(), arbitrator);
|
|
||||||
}
|
|
||||||
} catch (ClassNotFoundException | IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
log.error("Get arbitrators from DHT failed with exception:" + e.getMessage());
|
|
||||||
errorMessageHandler.handleErrorMessage("Get arbitrators from DHT failed with exception:" + e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
executor.execute(() -> {
|
|
||||||
resultHandler.handleResult(arbitratorsMap);
|
|
||||||
listeners.stream().forEach(listener -> listener.onAllArbitratorsLoaded(arbitratorsMap));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.error("Get arbitrators from DHT failed with reason:" + future.failedReason());
|
|
||||||
errorMessageHandler.handleErrorMessage("Get arbitrators from DHT failed with reason:" + future.failedReason());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Event Listeners
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public void addListener(Listener listener) {
|
|
||||||
listeners.add(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeListener(Listener listener) {
|
|
||||||
listeners.remove(listener);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -17,75 +17,59 @@
|
|||||||
|
|
||||||
package io.bitsquare.btc;
|
package io.bitsquare.btc;
|
||||||
|
|
||||||
import org.bitcoinj.core.Address;
|
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
import org.bitcoinj.core.NetworkParameters;
|
|
||||||
import org.bitcoinj.core.Transaction;
|
|
||||||
import org.bitcoinj.core.TransactionConfidence;
|
|
||||||
import org.bitcoinj.core.TransactionOutput;
|
|
||||||
import org.bitcoinj.wallet.CoinSelection;
|
|
||||||
import org.bitcoinj.wallet.DefaultCoinSelector;
|
|
||||||
|
|
||||||
import com.google.common.annotations.VisibleForTesting;
|
import com.google.common.annotations.VisibleForTesting;
|
||||||
|
import org.bitcoinj.core.*;
|
||||||
import java.math.BigInteger;
|
import org.bitcoinj.wallet.CoinSelection;
|
||||||
|
import org.bitcoinj.wallet.CoinSelector;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collections;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.math.BigInteger;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class implements a {@link org.bitcoinj.wallet.CoinSelector} which attempts to get the highest priority
|
* We use a specialized version of the CoinSelector based on the DefaultCoinSelector implementation.
|
||||||
* possible. This means that the transaction is the most likely to get confirmed. Note that this means we may end up
|
* We lookup for spendable outputs which matches our address of our addressEntry.
|
||||||
* "spending" more priority than would be required to get the transaction we are creating confirmed.
|
|
||||||
*/
|
*/
|
||||||
class AddressBasedCoinSelector extends DefaultCoinSelector {
|
class AddressBasedCoinSelector implements CoinSelector {
|
||||||
private static final Logger log = LoggerFactory.getLogger(AddressBasedCoinSelector.class);
|
private static final Logger log = LoggerFactory.getLogger(AddressBasedCoinSelector.class);
|
||||||
private final NetworkParameters params;
|
private final NetworkParameters params;
|
||||||
private final AddressEntry addressEntry;
|
private final AddressEntry addressEntry;
|
||||||
private final boolean includePending;
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Constructor
|
// Constructor
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public AddressBasedCoinSelector(NetworkParameters params, AddressEntry addressEntry, @SuppressWarnings("SameParameterValue") boolean includePending) {
|
public AddressBasedCoinSelector(NetworkParameters params, AddressEntry addressEntry) {
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.addressEntry = addressEntry;
|
this.addressEntry = addressEntry;
|
||||||
this.includePending = includePending;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
|
||||||
@VisibleForTesting
|
@VisibleForTesting
|
||||||
static void sortOutputs(ArrayList<TransactionOutput> outputs) {
|
static void sortOutputs(ArrayList<TransactionOutput> outputs) {
|
||||||
Collections.sort(outputs, (a, b) -> {
|
Collections.sort(outputs, new Comparator<TransactionOutput>() {
|
||||||
int depth1 = 0;
|
@Override
|
||||||
int depth2 = 0;
|
public int compare(TransactionOutput a, TransactionOutput b) {
|
||||||
assert a.getParentTransaction() != null;
|
int depth1 = a.getParentTransactionDepthInBlocks();
|
||||||
assert b.getParentTransaction() != null;
|
int depth2 = b.getParentTransactionDepthInBlocks();
|
||||||
TransactionConfidence conf1 = a.getParentTransaction().getConfidence();
|
Coin aValue = a.getValue();
|
||||||
TransactionConfidence conf2 = b.getParentTransaction().getConfidence();
|
Coin bValue = b.getValue();
|
||||||
if (conf1.getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
|
BigInteger aCoinDepth = BigInteger.valueOf(aValue.value).multiply(BigInteger.valueOf(depth1));
|
||||||
depth1 = conf1.getDepthInBlocks();
|
BigInteger bCoinDepth = BigInteger.valueOf(bValue.value).multiply(BigInteger.valueOf(depth2));
|
||||||
if (conf2.getConfidenceType() == TransactionConfidence.ConfidenceType.BUILDING)
|
int c1 = bCoinDepth.compareTo(aCoinDepth);
|
||||||
depth2 = conf2.getDepthInBlocks();
|
if (c1 != 0) return c1;
|
||||||
Coin aValue = a.getValue();
|
// The "coin*days" destroyed are equal, sort by value alone to get the lowest transaction size.
|
||||||
Coin bValue = b.getValue();
|
int c2 = bValue.compareTo(aValue);
|
||||||
BigInteger aCoinDepth = BigInteger.valueOf(aValue.value).multiply(BigInteger.valueOf(depth1));
|
if (c2 != 0) return c2;
|
||||||
BigInteger bCoinDepth = BigInteger.valueOf(bValue.value).multiply(BigInteger.valueOf(depth2));
|
// They are entirely equivalent (possibly pending) so sort by hash to ensure a total ordering.
|
||||||
int c1 = bCoinDepth.compareTo(aCoinDepth);
|
checkNotNull(a.getParentTransactionHash(), "a.getParentTransactionHash() must not be null");
|
||||||
if (c1 != 0) return c1;
|
checkNotNull(b.getParentTransactionHash(), "b.getParentTransactionHash() must not be null");
|
||||||
// The "coin*days" destroyed are equal, sort by value alone to get the lowest transaction size.
|
BigInteger aHash = a.getParentTransactionHash().toBigInteger();
|
||||||
int c2 = bValue.compareTo(aValue);
|
BigInteger bHash = b.getParentTransactionHash().toBigInteger();
|
||||||
if (c2 != 0) return c2;
|
return aHash.compareTo(bHash);
|
||||||
// They are entirely equivalent (possibly pending) so sort by hash to ensure a total ordering.
|
}
|
||||||
BigInteger aHash = a.getParentTransaction().getHash().toBigInteger();
|
|
||||||
BigInteger bHash = b.getParentTransaction().getHash().toBigInteger();
|
|
||||||
return aHash.compareTo(bHash);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,42 +78,16 @@ class AddressBasedCoinSelector extends DefaultCoinSelector {
|
|||||||
TransactionConfidence confidence = tx.getConfidence();
|
TransactionConfidence confidence = tx.getConfidence();
|
||||||
TransactionConfidence.ConfidenceType type = confidence.getConfidenceType();
|
TransactionConfidence.ConfidenceType type = confidence.getConfidenceType();
|
||||||
|
|
||||||
// TODO It might be risky to accept 0 confirmation tx from the network with only > 1 numBroadcastPeers
|
|
||||||
// Need to be tested in testnet and mainnet
|
|
||||||
// We need to handle cases when malleability happens or tx get lost and have not been successful propagated
|
|
||||||
/* return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
|
|
||||||
type.equals(TransactionConfidence.ConfidenceType.PENDING) &&
|
|
||||||
// we accept network tx without confirmations and numBroadcastPeers > 0
|
|
||||||
//confidence.getSource().equals(TransactionConfidence.Source.SELF) &&
|
|
||||||
// In regtest mode we expect to have only one peer, so we won't see transactions propagate.
|
|
||||||
// TODO: The value 1 below dates from a time when transactions we broadcast *to* were
|
|
||||||
// counted, set to 0
|
|
||||||
(confidence.numBroadcastPeers() > 1 || tx.getParams() == RegTestParams.get());*/
|
|
||||||
|
|
||||||
log.debug("numBroadcastPeers = " + confidence.numBroadcastPeers());
|
log.debug("numBroadcastPeers = " + confidence.numBroadcastPeers());
|
||||||
// TODO at testnet we got confidence.numBroadcastPeers()=0 -> probably because we use chained unconfirmed tx
|
|
||||||
// investigate further
|
|
||||||
return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
|
return type.equals(TransactionConfidence.ConfidenceType.BUILDING) ||
|
||||||
type.equals(TransactionConfidence.ConfidenceType.PENDING);
|
type.equals(TransactionConfidence.ConfidenceType.PENDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isInBlockChain(Transaction tx) {
|
|
||||||
// Only pick chain-included transactions.
|
|
||||||
TransactionConfidence confidence = tx.getConfidence();
|
|
||||||
TransactionConfidence.ConfidenceType type = confidence.getConfidenceType();
|
|
||||||
return type.equals(TransactionConfidence.ConfidenceType.BUILDING);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sub-classes can override this to just customize whether transactions are usable, but keep age sorting.
|
* Sub-classes can override this to just customize whether transactions are usable, but keep age sorting.
|
||||||
*/
|
*/
|
||||||
protected boolean shouldSelect(Transaction tx) {
|
protected boolean shouldSelect(Transaction tx) {
|
||||||
if (includePending) {
|
return isInBlockChainOrPending(tx);
|
||||||
return isInBlockChainOrPending(tx);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
return isInBlockChain(tx);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean matchesRequiredAddress(TransactionOutput transactionOutput) {
|
private boolean matchesRequiredAddress(TransactionOutput transactionOutput) {
|
||||||
@ -151,9 +109,9 @@ class AddressBasedCoinSelector extends DefaultCoinSelector {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public CoinSelection select(Coin target, List<TransactionOutput> candidates) {
|
public CoinSelection select(Coin target, List<TransactionOutput> candidates) {
|
||||||
log.debug("candidates.size: " + candidates.size());
|
log.trace("candidates.size: " + candidates.size());
|
||||||
long targetAsLong = target.longValue();
|
long targetAsLong = target.longValue();
|
||||||
log.debug("value needed: " + targetAsLong);
|
log.trace("value needed: " + targetAsLong);
|
||||||
HashSet<TransactionOutput> selected = new HashSet<>();
|
HashSet<TransactionOutput> selected = new HashSet<>();
|
||||||
// Sort the inputs by age*value so we get the highest "coindays" spent.
|
// Sort the inputs by age*value so we get the highest "coindays" spent.
|
||||||
// TODO: Consider changing the wallets internal format to track just outputs and keep them ordered.
|
// TODO: Consider changing the wallets internal format to track just outputs and keep them ordered.
|
||||||
|
@ -18,37 +18,58 @@
|
|||||||
package io.bitsquare.btc;
|
package io.bitsquare.btc;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
|
|
||||||
import org.bitcoinj.core.Address;
|
import org.bitcoinj.core.Address;
|
||||||
import org.bitcoinj.core.NetworkParameters;
|
import org.bitcoinj.core.NetworkParameters;
|
||||||
import org.bitcoinj.crypto.DeterministicKey;
|
import org.bitcoinj.crypto.DeterministicKey;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Is a minimalistic wallet abstraction used to separate transactions between different activities like:
|
* Every trade use a addressEntry with a dedicated address for all transactions related to the trade.
|
||||||
* Registration, trade and arbiter deposit.
|
* That way we have a kind of separated trade wallet, isolated from other transactions and avoiding coin merge.
|
||||||
|
* If we would not avoid coin merge the user would lose privacy between trades.
|
||||||
*/
|
*/
|
||||||
public class AddressEntry implements Serializable {
|
public class AddressEntry implements Serializable {
|
||||||
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
||||||
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
|
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
|
||||||
|
|
||||||
// that will be restored from the wallet at deserialization
|
private transient static final Logger log = LoggerFactory.getLogger(AddressEntry.class);
|
||||||
|
|
||||||
|
public enum Context {
|
||||||
|
TRADE,
|
||||||
|
ARBITRATOR
|
||||||
|
}
|
||||||
|
|
||||||
|
// keyPair can be null in case the object is created from deserialization as it is transient.
|
||||||
|
// It will be restored when the wallet is ready at setDeterministicKey
|
||||||
|
// So after startup it never must be null
|
||||||
|
@Nullable
|
||||||
private transient DeterministicKey keyPair;
|
private transient DeterministicKey keyPair;
|
||||||
|
|
||||||
private final String offerId;
|
// Only set if its a TRADE Context
|
||||||
|
@Nullable
|
||||||
|
private String offerId;
|
||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final byte[] pubKey;
|
private final byte[] pubKey;
|
||||||
private final byte[] pubKeyHash;
|
private final byte[] pubKeyHash;
|
||||||
private final NetworkParameters params;
|
private final NetworkParameters params;
|
||||||
|
|
||||||
public AddressEntry(DeterministicKey keyPair, NetworkParameters params, @SuppressWarnings("SameParameterValue") Context context) {
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Constructor, initialization
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// If created without offerId (arbitrator)
|
||||||
|
public AddressEntry(DeterministicKey keyPair, NetworkParameters params, Context context) {
|
||||||
this(keyPair, params, context, null);
|
this(keyPair, params, context, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddressEntry(DeterministicKey keyPair, NetworkParameters params, Context context, String offerId) {
|
// If created with offerId
|
||||||
|
public AddressEntry(DeterministicKey keyPair, NetworkParameters params, Context context, @Nullable String offerId) {
|
||||||
this.keyPair = keyPair;
|
this.keyPair = keyPair;
|
||||||
this.params = params;
|
this.params = params;
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@ -58,29 +79,44 @@ public class AddressEntry implements Serializable {
|
|||||||
pubKeyHash = keyPair.getPubKeyHash();
|
pubKeyHash = keyPair.getPubKeyHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Set after wallet is ready
|
||||||
|
public void setDeterministicKey(DeterministicKey deterministicKey) {
|
||||||
|
this.keyPair = deterministicKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Getters
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public String getOfferId() {
|
public String getOfferId() {
|
||||||
return offerId;
|
return offerId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// For display we usually only display the first 8 characters.
|
||||||
|
@Nullable
|
||||||
|
public String getShortOfferId() {
|
||||||
|
return offerId != null ? offerId.substring(0, 8) : null;
|
||||||
|
}
|
||||||
|
|
||||||
public Context getContext() {
|
public Context getContext() {
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public String getAddressString() {
|
public String getAddressString() {
|
||||||
return getAddress().toString();
|
return getAddress() != null ? getAddress().toString() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@Nullable
|
||||||
public DeterministicKey getKeyPair() {
|
public DeterministicKey getKeyPair() {
|
||||||
return keyPair;
|
return keyPair != null ? keyPair : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
public Address getAddress() {
|
public Address getAddress() {
|
||||||
return keyPair.toAddress(params);
|
return keyPair != null ? keyPair.toAddress(params) : null;
|
||||||
}
|
|
||||||
|
|
||||||
public void setDeterministicKey(DeterministicKey deterministicKey) {
|
|
||||||
this.keyPair = deterministicKey;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public byte[] getPubKeyHash() {
|
public byte[] getPubKeyHash() {
|
||||||
@ -91,21 +127,12 @@ public class AddressEntry implements Serializable {
|
|||||||
return pubKey;
|
return pubKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Context {
|
|
||||||
REGISTRATION_FEE,
|
|
||||||
TRADE,
|
|
||||||
ARBITRATOR_DEPOSIT
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "AddressEntry{" +
|
return "AddressEntry{" +
|
||||||
"offerId='" + offerId + '\'' +
|
"offerId='" + offerId + '\'' +
|
||||||
", context=" + context +
|
", context=" + context +
|
||||||
", address=" + getAddressString() +
|
", address=" + getAddressString() +
|
||||||
/* ", pubKey=" + Arrays.toString(pubKey) +
|
|
||||||
", pubKeyHash=" + Arrays.toString(pubKeyHash) +
|
|
||||||
", params=" + params +*/
|
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,21 +17,20 @@
|
|||||||
|
|
||||||
package io.bitsquare.btc;
|
package io.bitsquare.btc;
|
||||||
|
|
||||||
|
import com.google.inject.Inject;
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
|
|
||||||
import org.bitcoinj.core.Wallet;
|
import org.bitcoinj.core.Wallet;
|
||||||
import org.bitcoinj.crypto.DeterministicKey;
|
import org.bitcoinj.crypto.DeterministicKey;
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The List supporting our persistence solution.
|
||||||
|
*/
|
||||||
public class AddressEntryList extends ArrayList<AddressEntry> implements Serializable {
|
public class AddressEntryList extends ArrayList<AddressEntry> implements Serializable {
|
||||||
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
||||||
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
|
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
|
||||||
@ -53,36 +52,33 @@ public class AddressEntryList extends ArrayList<AddressEntry> implements Seriali
|
|||||||
AddressEntryList persisted = storage.initAndGetPersisted(this);
|
AddressEntryList persisted = storage.initAndGetPersisted(this);
|
||||||
if (persisted != null) {
|
if (persisted != null) {
|
||||||
for (AddressEntry addressEntry : persisted) {
|
for (AddressEntry addressEntry : persisted) {
|
||||||
addressEntry.setDeterministicKey((DeterministicKey) wallet.findKeyFromPubHash(addressEntry.getPubKeyHash()));
|
DeterministicKey keyFromPubHash = (DeterministicKey) wallet.findKeyFromPubHash(addressEntry.getPubKeyHash());
|
||||||
this.add(addressEntry);
|
if (keyFromPubHash != null) {
|
||||||
|
addressEntry.setDeterministicKey(keyFromPubHash);
|
||||||
|
add(addressEntry);
|
||||||
|
} else {
|
||||||
|
log.warn("Key from addressEntry not found in that wallet " + addressEntry.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
add(new AddressEntry(wallet.freshReceiveKey(), wallet.getParams(), AddressEntry.Context.ARBITRATOR));
|
||||||
// First time create registrationAddressEntry
|
storage.queueUpForSave();
|
||||||
createRegistrationAddressEntry();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddressEntry getNewAddressEntry(AddressEntry.Context context, String offerId) {
|
public AddressEntry getNewAddressEntry(AddressEntry.Context context, String offerId) {
|
||||||
log.trace("getNewAddressEntry called with offerId " + offerId);
|
log.trace("getNewAddressEntry called with offerId " + offerId);
|
||||||
DeterministicKey key = wallet.freshReceiveKey();
|
AddressEntry addressEntry = new AddressEntry(wallet.freshReceiveKey(), wallet.getParams(), context, offerId);
|
||||||
AddressEntry addressEntry = new AddressEntry(key, wallet.getParams(), context, offerId);
|
|
||||||
add(addressEntry);
|
add(addressEntry);
|
||||||
storage.queueUpForSave();
|
storage.queueUpForSave();
|
||||||
return addressEntry;
|
return addressEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createRegistrationAddressEntry() {
|
|
||||||
DeterministicKey registrationKey = wallet.currentReceiveKey();
|
|
||||||
AddressEntry registrationAddressEntry = new AddressEntry(registrationKey, wallet.getParams(), AddressEntry.Context.REGISTRATION_FEE);
|
|
||||||
add(registrationAddressEntry);
|
|
||||||
storage.queueUpForSave();
|
|
||||||
}
|
|
||||||
|
|
||||||
public AddressEntry getRegistrationAddressEntry() {
|
public AddressEntry getArbitratorAddressEntry() {
|
||||||
if (isEmpty())
|
if (size() > 0)
|
||||||
createRegistrationAddressEntry();
|
return get(0);
|
||||||
|
else
|
||||||
return get(0);
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,21 +17,17 @@
|
|||||||
|
|
||||||
package io.bitsquare.btc;
|
package io.bitsquare.btc;
|
||||||
|
|
||||||
import io.bitsquare.BitsquareModule;
|
|
||||||
|
|
||||||
import com.google.inject.Injector;
|
|
||||||
import com.google.inject.Singleton;
|
import com.google.inject.Singleton;
|
||||||
|
import io.bitsquare.app.AppModule;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import org.springframework.core.env.Environment;
|
|
||||||
|
|
||||||
import static com.google.inject.name.Names.named;
|
import static com.google.inject.name.Names.named;
|
||||||
|
|
||||||
public class BitcoinModule extends BitsquareModule {
|
public class BitcoinModule extends AppModule {
|
||||||
private static final Logger log = LoggerFactory.getLogger(BitcoinModule.class);
|
private static final Logger log = LoggerFactory.getLogger(BitcoinModule.class);
|
||||||
|
|
||||||
public BitcoinModule(Environment env) {
|
public BitcoinModule(Environment env) {
|
||||||
@ -54,13 +50,6 @@ public class BitcoinModule extends BitsquareModule {
|
|||||||
bind(AddressEntryList.class).in(Singleton.class);
|
bind(AddressEntryList.class).in(Singleton.class);
|
||||||
bind(TradeWalletService.class).in(Singleton.class);
|
bind(TradeWalletService.class).in(Singleton.class);
|
||||||
bind(WalletService.class).in(Singleton.class);
|
bind(WalletService.class).in(Singleton.class);
|
||||||
bind(BlockChainService.class).in(Singleton.class);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doClose(Injector injector) {
|
|
||||||
log.trace("doClose " + getClass().getSimpleName());
|
|
||||||
injector.getInstance(WalletService.class).shutDown();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,76 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.btc;
|
|
||||||
|
|
||||||
import io.bitsquare.fiat.FiatAccount;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A service delivers blockchain functionality from the BitcoinJ library.
|
|
||||||
*/
|
|
||||||
@SuppressWarnings({"UnusedDeclaration", "UnusedParameters"})
|
|
||||||
public class BlockChainService {
|
|
||||||
@Inject
|
|
||||||
public BlockChainService() {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO
|
|
||||||
@SuppressWarnings("SameReturnValue")
|
|
||||||
public boolean isAccountBlackListed(String accountId, FiatAccount fiatAccount) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO
|
|
||||||
@SuppressWarnings("SameReturnValue")
|
|
||||||
public boolean verifyAccountRegistration() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("SameReturnValue")
|
|
||||||
private boolean findAddressInBlockChain(String address) {
|
|
||||||
// TODO lookup for address in blockchain
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("SameReturnValue")
|
|
||||||
private byte[] getDataForTxWithAddress(String address) {
|
|
||||||
// TODO return data after OP_RETURN
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("SameReturnValue")
|
|
||||||
private boolean isFeePayed(String address) {
|
|
||||||
// TODO check if fee is paid
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("SameReturnValue")
|
|
||||||
private boolean isAccountIDBlacklisted(String accountID) {
|
|
||||||
// TODO check if accountID is on blacklist
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("SameReturnValue")
|
|
||||||
private boolean isFiatAccountBlacklisted(FiatAccount fiatAccount) {
|
|
||||||
// TODO check if accountID is on blacklist
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,68 +17,39 @@
|
|||||||
|
|
||||||
package io.bitsquare.btc;
|
package io.bitsquare.btc;
|
||||||
|
|
||||||
import io.bitsquare.BitsquareException;
|
|
||||||
import io.bitsquare.user.Preferences;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Address;
|
|
||||||
import org.bitcoinj.core.AddressFormatException;
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
import org.bitcoinj.core.Transaction;
|
import org.bitcoinj.core.Wallet;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
public class FeePolicy {
|
public class FeePolicy {
|
||||||
|
|
||||||
public static final Coin TX_FEE = Transaction.REFERENCE_DEFAULT_MIN_TX_FEE; // dropped down to 0.00001 BTC
|
// Official min. fee and fee per kiloByte dropped down to 0.00001 BTC / Coin.valueOf(1000) / 1000 satoshis, but as there are reported problems with
|
||||||
|
// confirmation we use a hgher value.
|
||||||
|
// The should also help to avoid problems when the tx size is larger as the standard (e.g. if the user does not pay
|
||||||
|
// in with one transaction but several tx). We don't do a dynamically fee calculation as we need predictable amounts, so that should help to get a larger
|
||||||
|
// headroom.
|
||||||
|
// Andreas Schildbach reported problems with confirmation and increased the fee/offered UI side fee setting.
|
||||||
|
|
||||||
|
// http://www.cointape.com/
|
||||||
|
// The fastest and cheapest transaction fee is currently 50 satoshis/byte, shown in green at the top.
|
||||||
|
// For the average transaction size of 597 bytes, this results in a fee of 298 bits (0.298 mBTC). -> 0.0003 BTC or Coin.valueOf(30000);
|
||||||
|
|
||||||
// TODO: Change REGISTRATION_FEE to 0.00001 (See https://github.com/bitsquare/bitsquare/issues/228)
|
// trade fee tx: 226 bytes
|
||||||
public static final Coin REGISTRATION_FEE = TX_FEE.add(TX_FEE);
|
// deposit tx: 336 bytes
|
||||||
public static final Coin CREATE_OFFER_FEE = Coin.valueOf(1000000); // 0.01 BTC
|
// payout tx: 371 bytes
|
||||||
|
// disputed payout tx: 408 bytes -> 20400 satoshis with 50 satoshis/byte
|
||||||
|
|
||||||
|
// Other good source is: https://tradeblock.com/blockchain 15-100 satoshis/byte
|
||||||
|
|
||||||
|
public static final Coin TX_FEE = Coin.valueOf(30000); // 0.0003 BTC about 0.06 EUR @ 200 EUR/BTC: about 90 satoshi /byte
|
||||||
|
|
||||||
|
static {
|
||||||
|
// we use our fee as default fee
|
||||||
|
Wallet.SendRequest.DEFAULT_FEE_PER_KB = FeePolicy.TX_FEE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Coin DUST = Coin.valueOf(546);
|
||||||
|
|
||||||
|
public static final Coin CREATE_OFFER_FEE = Coin.valueOf(100000); // 0.001 BTC 0.1% of 1 BTC about 0.2 EUR @ 200 EUR/BTC
|
||||||
public static final Coin TAKE_OFFER_FEE = CREATE_OFFER_FEE;
|
public static final Coin TAKE_OFFER_FEE = CREATE_OFFER_FEE;
|
||||||
|
public static final Coin SECURITY_DEPOSIT = Coin.valueOf(10000000); // 0.1 BTC; about 20 EUR @ 200 EUR/BTC
|
||||||
private final BitcoinNetwork bitcoinNetwork;
|
|
||||||
private final String createOfferFeeAddress;
|
|
||||||
private final String takeOfferFeeAddress;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public FeePolicy(Preferences preferences) {
|
|
||||||
this.bitcoinNetwork = preferences.getBitcoinNetwork();
|
|
||||||
|
|
||||||
switch (bitcoinNetwork) {
|
|
||||||
case TESTNET:
|
|
||||||
createOfferFeeAddress = "mopJDiHncoveyy7S7FZTUNVbrCxazxvGrE";
|
|
||||||
takeOfferFeeAddress = "mopJDiHncoveyy7S7FZTUNVbrCxazxvGrE";
|
|
||||||
break;
|
|
||||||
case MAINNET:
|
|
||||||
// bitsquare donation address used for the moment...
|
|
||||||
createOfferFeeAddress = "1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7";
|
|
||||||
takeOfferFeeAddress = "1BVxNn3T12veSK6DgqwU4Hdn7QHcDDRag7";
|
|
||||||
break;
|
|
||||||
case REGTEST:
|
|
||||||
createOfferFeeAddress = "mkNW1omJFA7RD3AZ94mfKqubRff2gx21KE";
|
|
||||||
takeOfferFeeAddress = "mkNW1omJFA7RD3AZ94mfKqubRff2gx21KE";
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new BitsquareException("Unknown bitcoin network: %s", bitcoinNetwork);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO get address form arbitrator list
|
|
||||||
public Address getAddressForCreateOfferFee() {
|
|
||||||
try {
|
|
||||||
return new Address(bitcoinNetwork.getParameters(), createOfferFeeAddress);
|
|
||||||
} catch (AddressFormatException ex) {
|
|
||||||
throw new BitsquareException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO get address form the intersection of both traders arbitrator lists
|
|
||||||
public Address getAddressForTakeOfferFee() {
|
|
||||||
try {
|
|
||||||
return new Address(bitcoinNetwork.getParameters(), takeOfferFeeAddress);
|
|
||||||
} catch (AddressFormatException ex) {
|
|
||||||
throw new BitsquareException(ex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -20,12 +20,9 @@ package io.bitsquare.btc;
|
|||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
import org.bitcoinj.core.Transaction;
|
import org.bitcoinj.core.Transaction;
|
||||||
|
|
||||||
// Lets see if we get more restriction otherwise move it to other class
|
|
||||||
public class Restrictions {
|
public class Restrictions {
|
||||||
public static final Coin MIN_TRADE_AMOUNT = Coin.parseCoin("0.0001");
|
public static final Coin MIN_TRADE_AMOUNT = Coin.parseCoin("0.0001");
|
||||||
public static final Coin MAX_TRADE_AMOUNT = Coin.parseCoin("10");
|
public static final Coin MAX_TRADE_AMOUNT = Coin.parseCoin("1");
|
||||||
public static final Coin MIN_SECURITY_DEPOSIT = Coin.parseCoin("0.0001");
|
|
||||||
|
|
||||||
|
|
||||||
public static boolean isMinSpendableAmount(Coin amount) {
|
public static boolean isMinSpendableAmount(Coin amount) {
|
||||||
return amount != null && amount.compareTo(FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT)) > 0;
|
return amount != null && amount.compareTo(FeePolicy.TX_FEE.add(Transaction.MIN_NONDUST_OUTPUT)) > 0;
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -17,89 +17,56 @@
|
|||||||
|
|
||||||
package io.bitsquare.btc;
|
package io.bitsquare.btc;
|
||||||
|
|
||||||
import io.bitsquare.btc.listeners.AddressConfidenceListener;
|
|
||||||
import io.bitsquare.btc.listeners.BalanceListener;
|
|
||||||
import io.bitsquare.btc.listeners.TxConfidenceListener;
|
|
||||||
import io.bitsquare.crypto.CryptoService;
|
|
||||||
import io.bitsquare.user.Preferences;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.AbstractWalletEventListener;
|
|
||||||
import org.bitcoinj.core.Address;
|
|
||||||
import org.bitcoinj.core.AddressFormatException;
|
|
||||||
import org.bitcoinj.core.Block;
|
|
||||||
import org.bitcoinj.core.Coin;
|
|
||||||
import org.bitcoinj.core.DownloadProgressTracker;
|
|
||||||
import org.bitcoinj.core.FilteredBlock;
|
|
||||||
import org.bitcoinj.core.GetDataMessage;
|
|
||||||
import org.bitcoinj.core.InsufficientMoneyException;
|
|
||||||
import org.bitcoinj.core.Message;
|
|
||||||
import org.bitcoinj.core.NetworkParameters;
|
|
||||||
import org.bitcoinj.core.Peer;
|
|
||||||
import org.bitcoinj.core.PeerAddress;
|
|
||||||
import org.bitcoinj.core.PeerEventListener;
|
|
||||||
import org.bitcoinj.core.Transaction;
|
|
||||||
import org.bitcoinj.core.TransactionConfidence;
|
|
||||||
import org.bitcoinj.core.TransactionInput;
|
|
||||||
import org.bitcoinj.core.TransactionOutput;
|
|
||||||
import org.bitcoinj.core.Wallet;
|
|
||||||
import org.bitcoinj.core.WalletEventListener;
|
|
||||||
import org.bitcoinj.kits.WalletAppKit;
|
|
||||||
import org.bitcoinj.params.MainNetParams;
|
|
||||||
import org.bitcoinj.params.RegTestParams;
|
|
||||||
import org.bitcoinj.params.TestNet3Params;
|
|
||||||
import org.bitcoinj.script.ScriptBuilder;
|
|
||||||
import org.bitcoinj.utils.Threading;
|
|
||||||
|
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
import com.google.common.util.concurrent.Futures;
|
import com.google.common.util.concurrent.Futures;
|
||||||
import com.google.common.util.concurrent.Service;
|
import com.google.common.util.concurrent.Service;
|
||||||
|
import io.bitsquare.btc.listeners.AddressConfidenceListener;
|
||||||
|
import io.bitsquare.btc.listeners.BalanceListener;
|
||||||
|
import io.bitsquare.btc.listeners.TxConfidenceListener;
|
||||||
|
import io.bitsquare.common.UserThread;
|
||||||
|
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
||||||
|
import io.bitsquare.common.handlers.ExceptionHandler;
|
||||||
|
import io.bitsquare.common.handlers.ResultHandler;
|
||||||
|
import io.bitsquare.user.Preferences;
|
||||||
|
import javafx.beans.property.*;
|
||||||
|
import org.bitcoinj.core.*;
|
||||||
|
import org.bitcoinj.kits.WalletAppKit;
|
||||||
|
import org.bitcoinj.params.MainNetParams;
|
||||||
|
import org.bitcoinj.params.RegTestParams;
|
||||||
|
import org.bitcoinj.params.TestNet3Params;
|
||||||
|
import org.bitcoinj.utils.Threading;
|
||||||
|
import org.bitcoinj.wallet.DeterministicSeed;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.reactfx.util.FxTimer;
|
||||||
|
import org.reactfx.util.Timer;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.UnknownHostException;
|
import java.net.UnknownHostException;
|
||||||
|
import java.time.Duration;
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Date;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Optional;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
import java.util.concurrent.CopyOnWriteArrayList;
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.TimeoutException;
|
import java.util.concurrent.TimeoutException;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
/**
|
||||||
|
* WalletService handles all non trade specific wallet and bitcoin related services.
|
||||||
import javax.inject.Inject;
|
* It startup the wallet app kit and initialized the wallet.
|
||||||
import javax.inject.Named;
|
*/
|
||||||
|
|
||||||
import javafx.beans.property.DoubleProperty;
|
|
||||||
import javafx.beans.property.IntegerProperty;
|
|
||||||
import javafx.beans.property.ReadOnlyDoubleProperty;
|
|
||||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
|
||||||
import javafx.beans.property.SimpleDoubleProperty;
|
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import rx.Observable;
|
|
||||||
import rx.subjects.BehaviorSubject;
|
|
||||||
import rx.subjects.Subject;
|
|
||||||
|
|
||||||
import static org.bitcoinj.script.ScriptOpCodes.OP_RETURN;
|
|
||||||
|
|
||||||
public class WalletService {
|
public class WalletService {
|
||||||
private static final Logger log = LoggerFactory.getLogger(WalletService.class);
|
private static final Logger log = LoggerFactory.getLogger(WalletService.class);
|
||||||
|
|
||||||
public static final String DIR_KEY = "wallet.dir";
|
public static final String DIR_KEY = "wallet.dir";
|
||||||
public static final String PREFIX_KEY = "wallet.prefix";
|
public static final String PREFIX_KEY = "wallet.prefix";
|
||||||
private static final long STARTUP_TIMEOUT = 60;
|
private static final long STARTUP_TIMEOUT = 60 * 1000;
|
||||||
|
|
||||||
private final List<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArrayList<>();
|
private final List<AddressConfidenceListener> addressConfidenceListeners = new CopyOnWriteArrayList<>();
|
||||||
private final List<TxConfidenceListener> txConfidenceListeners = new CopyOnWriteArrayList<>();
|
private final List<TxConfidenceListener> txConfidenceListeners = new CopyOnWriteArrayList<>();
|
||||||
@ -112,16 +79,15 @@ public class WalletService {
|
|||||||
private final TradeWalletService tradeWalletService;
|
private final TradeWalletService tradeWalletService;
|
||||||
private final AddressEntryList addressEntryList;
|
private final AddressEntryList addressEntryList;
|
||||||
private final NetworkParameters params;
|
private final NetworkParameters params;
|
||||||
private final CryptoService cryptoService;
|
|
||||||
private final File walletDir;
|
private final File walletDir;
|
||||||
private final String walletPrefix;
|
private final String walletPrefix;
|
||||||
private final UserAgent userAgent;
|
private final UserAgent userAgent;
|
||||||
|
|
||||||
private WalletAppKit walletAppKit;
|
private WalletAppKit walletAppKit;
|
||||||
private Wallet wallet;
|
private Wallet wallet;
|
||||||
private AddressEntry registrationAddressEntry;
|
private AddressEntry arbitratorAddressEntry;
|
||||||
private AddressEntry arbitratorDepositAddressEntry;
|
|
||||||
private final IntegerProperty numPeers = new SimpleIntegerProperty(0);
|
private final IntegerProperty numPeers = new SimpleIntegerProperty(0);
|
||||||
|
public final BooleanProperty shutDownDone = new SimpleBooleanProperty();
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -129,14 +95,12 @@ public class WalletService {
|
|||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
public WalletService(RegTestHost regTestHost, CryptoService cryptoService,
|
public WalletService(RegTestHost regTestHost, TradeWalletService tradeWalletService, AddressEntryList addressEntryList, UserAgent userAgent,
|
||||||
TradeWalletService tradeWalletService, AddressEntryList addressEntryList, UserAgent userAgent,
|
|
||||||
@Named(DIR_KEY) File walletDir, @Named(PREFIX_KEY) String walletPrefix, Preferences preferences) {
|
@Named(DIR_KEY) File walletDir, @Named(PREFIX_KEY) String walletPrefix, Preferences preferences) {
|
||||||
this.regTestHost = regTestHost;
|
this.regTestHost = regTestHost;
|
||||||
this.tradeWalletService = tradeWalletService;
|
this.tradeWalletService = tradeWalletService;
|
||||||
this.addressEntryList = addressEntryList;
|
this.addressEntryList = addressEntryList;
|
||||||
this.params = preferences.getBitcoinNetwork().getParameters();
|
this.params = preferences.getBitcoinNetwork().getParameters();
|
||||||
this.cryptoService = cryptoService;
|
|
||||||
this.walletDir = new File(walletDir, "bitcoin");
|
this.walletDir = new File(walletDir, "bitcoin");
|
||||||
this.walletPrefix = walletPrefix;
|
this.walletPrefix = walletPrefix;
|
||||||
this.userAgent = userAgent;
|
this.userAgent = userAgent;
|
||||||
@ -147,14 +111,18 @@ public class WalletService {
|
|||||||
// Public Methods
|
// Public Methods
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public Observable<Object> initialize(Executor executor) {
|
public void initialize(@Nullable DeterministicSeed seed, ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
|
||||||
Subject<Object, Object> status = BehaviorSubject.create();
|
|
||||||
|
|
||||||
// Tell bitcoinj to execute event handlers on the JavaFX UI thread. This keeps things simple and means
|
// Tell bitcoinj to execute event handlers on the JavaFX UI thread. This keeps things simple and means
|
||||||
// we cannot forget to switch threads when adding event handlers. Unfortunately, the DownloadListener
|
// we cannot forget to switch threads when adding event handlers. Unfortunately, the DownloadListener
|
||||||
// we give to the app kit is currently an exception and runs on a library thread. It'll get fixed in
|
// we give to the app kit is currently an exception and runs on a library thread. It'll get fixed in
|
||||||
// a future version.
|
// a future version.
|
||||||
Threading.USER_THREAD = executor;
|
|
||||||
|
Threading.USER_THREAD = UserThread.getExecutor();
|
||||||
|
|
||||||
|
Timer timeoutTimer = FxTimer.runLater(
|
||||||
|
Duration.ofMillis(STARTUP_TIMEOUT),
|
||||||
|
() -> exceptionHandler.handleException(new TimeoutException("Wallet did not initialize in " + STARTUP_TIMEOUT / 1000 + " seconds."))
|
||||||
|
);
|
||||||
|
|
||||||
// If seed is non-null it means we are restoring from backup.
|
// If seed is non-null it means we are restoring from backup.
|
||||||
walletAppKit = new WalletAppKit(params, walletDir, walletPrefix) {
|
walletAppKit = new WalletAppKit(params, walletDir, walletPrefix) {
|
||||||
@ -166,30 +134,70 @@ public class WalletService {
|
|||||||
if (params != RegTestParams.get())
|
if (params != RegTestParams.get())
|
||||||
walletAppKit.peerGroup().setMaxConnections(11);
|
walletAppKit.peerGroup().setMaxConnections(11);
|
||||||
walletAppKit.peerGroup().setBloomFilterFalsePositiveRate(0.00001);
|
walletAppKit.peerGroup().setBloomFilterFalsePositiveRate(0.00001);
|
||||||
initWallet();
|
wallet = walletAppKit.wallet();
|
||||||
|
wallet.addEventListener(walletEventListener);
|
||||||
|
|
||||||
|
addressEntryList.onWalletReady(wallet);
|
||||||
|
arbitratorAddressEntry = addressEntryList.getArbitratorAddressEntry();
|
||||||
|
|
||||||
|
walletAppKit.peerGroup().addEventListener(new PeerEventListener() {
|
||||||
|
@Override
|
||||||
|
public void onPeersDiscovered(Set<PeerAddress> peerAddresses) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBlocksDownloaded(Peer peer, Block block, FilteredBlock filteredBlock, int blocksLeft) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChainDownloadStarted(Peer peer, int blocksLeft) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPeerConnected(Peer peer, int peerCount) {
|
||||||
|
numPeers.set(peerCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPeerDisconnected(Peer peer, int peerCount) {
|
||||||
|
numPeers.set(peerCount);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Message onPreMessageReceived(Peer peer, Message m) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransaction(Peer peer, Transaction t) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
@Override
|
||||||
|
public List<Message> getData(Peer peer, GetDataMessage m) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// set after wallet is ready
|
// set after wallet is ready
|
||||||
tradeWalletService.setWalletAppKit(walletAppKit);
|
tradeWalletService.setWalletAppKit(walletAppKit);
|
||||||
|
timeoutTimer.stop();
|
||||||
status.onCompleted();
|
UserThread.execute(resultHandler::handleResult);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
// Now configure and start the appkit. This will take a second or two - we could show a temporary splash screen
|
// Now configure and start the appkit. This will take a second or two - we could show a temporary splash screen
|
||||||
// or progress widget to keep the user engaged whilst we initialise, but we don't.
|
// or progress widget to keep the user engaged whilst we initialise, but we don't.
|
||||||
if (params == RegTestParams.get()) {
|
if (params == RegTestParams.get()) {
|
||||||
log.debug("regTestHost " + regTestHost);
|
|
||||||
if (regTestHost == RegTestHost.REG_TEST_SERVER) {
|
if (regTestHost == RegTestHost.REG_TEST_SERVER) {
|
||||||
try {
|
try {
|
||||||
walletAppKit.setPeerNodes(new PeerAddress(InetAddress.getByName(RegTestHost.SERVER_IP), params.getPort()));
|
walletAppKit.setPeerNodes(new PeerAddress(InetAddress.getByName(RegTestHost.SERVER_IP), params.getPort()));
|
||||||
} catch (UnknownHostException e) {
|
} catch (UnknownHostException e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
}
|
} else if (regTestHost == RegTestHost.LOCALHOST) {
|
||||||
else if (regTestHost == RegTestHost.LOCALHOST) {
|
|
||||||
walletAppKit.connectToLocalHost(); // You should run a regtest mode bitcoind locally.}
|
walletAppKit.connectToLocalHost(); // You should run a regtest mode bitcoind locally.}
|
||||||
}
|
}
|
||||||
}
|
} else if (params == MainNetParams.get()) {
|
||||||
else if (params == MainNetParams.get()) {
|
|
||||||
// Checkpoints are block headers that ship inside our app: for a new user, we pick the last header
|
// Checkpoints are block headers that ship inside our app: for a new user, we pick the last header
|
||||||
// in the checkpoints file and then download the rest from the network. It makes things much faster.
|
// in the checkpoints file and then download the rest from the network. It makes things much faster.
|
||||||
// Checkpoint files are made using the BuildCheckpoints tool and usually we have to download the
|
// Checkpoint files are made using the BuildCheckpoints tool and usually we have to download the
|
||||||
@ -200,102 +208,46 @@ public class WalletService {
|
|||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.error(e.toString());
|
log.error(e.toString());
|
||||||
}
|
}
|
||||||
}
|
} else if (params == TestNet3Params.get()) {
|
||||||
else if (params == TestNet3Params.get()) {
|
|
||||||
walletAppKit.setCheckpoints(getClass().getResourceAsStream("/wallet/checkpoints.testnet"));
|
walletAppKit.setCheckpoints(getClass().getResourceAsStream("/wallet/checkpoints.testnet"));
|
||||||
}
|
}
|
||||||
|
|
||||||
walletAppKit.setDownloadListener(downloadListener)
|
walletAppKit.setDownloadListener(downloadListener)
|
||||||
.setBlockingStartup(false)
|
.setBlockingStartup(false)
|
||||||
.setUserAgent(userAgent.getName(), userAgent.getVersion());
|
.setUserAgent(userAgent.getName(), userAgent.getVersion())
|
||||||
|
.restoreWalletFromSeed(seed);
|
||||||
/*
|
|
||||||
// TODO restore from DeterministicSeed
|
|
||||||
if (seed != null)
|
|
||||||
walletAppKit.restoreWalletFromSeed(seed);
|
|
||||||
*/
|
|
||||||
|
|
||||||
walletAppKit.addListener(new Service.Listener() {
|
walletAppKit.addListener(new Service.Listener() {
|
||||||
@Override
|
@Override
|
||||||
public void failed(@NotNull Service.State from, @NotNull Throwable failure) {
|
public void failed(@NotNull Service.State from, @NotNull Throwable failure) {
|
||||||
walletAppKit = null;
|
walletAppKit = null;
|
||||||
log.error("walletAppKit failed");
|
log.error("walletAppKit failed");
|
||||||
status.onError(failure);
|
timeoutTimer.stop();
|
||||||
|
UserThread.execute(() -> exceptionHandler.handleException(failure));
|
||||||
}
|
}
|
||||||
}, Threading.USER_THREAD);
|
}, Threading.USER_THREAD);
|
||||||
walletAppKit.startAsync();
|
walletAppKit.startAsync();
|
||||||
return status.timeout(STARTUP_TIMEOUT, TimeUnit.SECONDS);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initWallet() {
|
|
||||||
wallet = walletAppKit.wallet();
|
|
||||||
wallet.addEventListener(walletEventListener);
|
|
||||||
|
|
||||||
addressEntryList.onWalletReady(wallet);
|
|
||||||
registrationAddressEntry = addressEntryList.getRegistrationAddressEntry();
|
|
||||||
|
|
||||||
walletAppKit.peerGroup().addEventListener(new PeerEventListener() {
|
|
||||||
@Override
|
|
||||||
public void onPeersDiscovered(Set<PeerAddress> peerAddresses) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBlocksDownloaded(Peer peer, Block block, FilteredBlock filteredBlock, int blocksLeft) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onChainDownloadStarted(Peer peer, int blocksLeft) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPeerConnected(Peer peer, int peerCount) {
|
|
||||||
log.trace("onPeerConnected " + peerCount);
|
|
||||||
Threading.USER_THREAD.execute(() -> numPeers.set(peerCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPeerDisconnected(Peer peer, int peerCount) {
|
|
||||||
log.trace("onPeerDisconnected " + peerCount);
|
|
||||||
Threading.USER_THREAD.execute(() -> numPeers.set(peerCount));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Message onPreMessageReceived(Peer peer, Message m) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTransaction(Peer peer, Transaction t) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
@Override
|
|
||||||
public List<Message> getData(Peer peer, GetDataMessage m) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void shutDown() {
|
public void shutDown() {
|
||||||
if (wallet != null)
|
if (wallet != null)
|
||||||
wallet.removeEventListener(walletEventListener);
|
wallet.removeEventListener(walletEventListener);
|
||||||
|
|
||||||
if (walletAppKit != null) {
|
if (walletAppKit != null) {
|
||||||
walletAppKit.stopAsync();
|
|
||||||
try {
|
try {
|
||||||
|
walletAppKit.stopAsync();
|
||||||
walletAppKit.awaitTerminated(5, TimeUnit.SECONDS);
|
walletAppKit.awaitTerminated(5, TimeUnit.SECONDS);
|
||||||
} catch (TimeoutException e) {
|
} catch (Throwable e) {
|
||||||
e.printStackTrace();
|
// ignore
|
||||||
log.error("walletAppKit.awaitTerminated not terminated after 5 sec. Error message: " + e.getMessage());
|
|
||||||
}
|
}
|
||||||
|
shutDownDone.set(true);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyDoubleProperty downloadPercentageProperty() {
|
public void restoreSeedWords(DeterministicSeed seed, ResultHandler resultHandler, ExceptionHandler exceptionHandler) {
|
||||||
return downloadListener.percentageProperty();
|
walletAppKit.stopAsync();
|
||||||
}
|
walletAppKit.awaitTerminated();
|
||||||
|
initialize(seed, resultHandler, exceptionHandler);
|
||||||
public Wallet getWallet() {
|
|
||||||
return wallet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -332,40 +284,26 @@ public class WalletService {
|
|||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Get AddressInfo objects
|
// AddressInfo
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public List<AddressEntry> getAddressEntryList() {
|
public List<AddressEntry> getAddressEntryList() {
|
||||||
return ImmutableList.copyOf(addressEntryList);
|
return ImmutableList.copyOf(addressEntryList);
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddressEntry getRegistrationAddressEntry() {
|
public AddressEntry getArbitratorAddressEntry() {
|
||||||
return registrationAddressEntry;
|
return arbitratorAddressEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
public AddressEntry getArbitratorDepositAddressEntry() {
|
public AddressEntry getAddressEntryByOfferId(String offerId) {
|
||||||
if (arbitratorDepositAddressEntry == null)
|
|
||||||
arbitratorDepositAddressEntry = addressEntryList.getNewAddressEntry(AddressEntry.Context.ARBITRATOR_DEPOSIT, null);
|
|
||||||
|
|
||||||
return arbitratorDepositAddressEntry;
|
|
||||||
}
|
|
||||||
|
|
||||||
public AddressEntry getAddressEntry(String offerId) {
|
|
||||||
log.trace("getAddressEntry called with offerId " + offerId);
|
|
||||||
Optional<AddressEntry> addressEntry = getAddressEntryList().stream().filter(e -> offerId.equals(e.getOfferId())).findFirst();
|
Optional<AddressEntry> addressEntry = getAddressEntryList().stream().filter(e -> offerId.equals(e.getOfferId())).findFirst();
|
||||||
|
|
||||||
if (addressEntry.isPresent())
|
if (addressEntry.isPresent())
|
||||||
return addressEntry.get();
|
return addressEntry.get();
|
||||||
else
|
else
|
||||||
return addressEntryList.getNewAddressEntry(AddressEntry.Context.TRADE, offerId);
|
return addressEntryList.getNewAddressEntry(AddressEntry.Context.TRADE, offerId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Optional<AddressEntry> getAddressEntryByAddress(String address) {
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Create new AddressInfo objects
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private Optional<AddressEntry> getAddressEntryByAddressString(String address) {
|
|
||||||
return getAddressEntryList().stream().filter(e -> address.equals(e.getAddressString())).findFirst();
|
return getAddressEntryList().stream().filter(e -> address.equals(e.getAddressString())).findFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,26 +383,22 @@ public class WalletService {
|
|||||||
transactionConfidence = confidence;
|
transactionConfidence = confidence;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
return transactionConfidence;
|
return transactionConfidence;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
|
||||||
public boolean isRegistrationFeeConfirmed() {
|
|
||||||
assert getRegistrationAddressEntry() != null;
|
|
||||||
TransactionConfidence transactionConfidence = getConfidenceForAddress(getRegistrationAddressEntry().getAddress());
|
|
||||||
return TransactionConfidence.ConfidenceType.BUILDING.equals(transactionConfidence.getConfidenceType());
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Balance
|
// Balance
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
// BalanceType.AVAILABLE
|
||||||
|
public Coin getAvailableBalance() {
|
||||||
|
return wallet != null ? wallet.getBalance(Wallet.BalanceType.AVAILABLE) : Coin.ZERO;
|
||||||
|
}
|
||||||
|
|
||||||
public Coin getBalanceForAddress(Address address) {
|
public Coin getBalanceForAddress(Address address) {
|
||||||
return wallet != null ? getBalance(wallet.calculateAllSpendCandidates(true), address) : Coin.ZERO;
|
return wallet != null ? getBalance(wallet.calculateAllSpendCandidates(), address) : Coin.ZERO;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Coin getBalance(List<TransactionOutput> transactionOutputs, Address address) {
|
private Coin getBalance(List<TransactionOutput> transactionOutputs, Address address) {
|
||||||
@ -479,97 +413,27 @@ public class WalletService {
|
|||||||
return balance;
|
return balance;
|
||||||
}
|
}
|
||||||
|
|
||||||
Coin getWalletBalance() {
|
|
||||||
return wallet.getBalance(Wallet.BalanceType.ESTIMATED);
|
|
||||||
}
|
|
||||||
|
|
||||||
Coin getRegistrationBalance() {
|
|
||||||
return getBalanceForAddress(getRegistrationAddressEntry().getAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Coin getArbitratorDepositBalance() {
|
|
||||||
return getBalanceForAddress(getArbitratorDepositAddressEntry().getAddress());
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
|
||||||
public boolean isRegistrationFeeBalanceNonZero() {
|
|
||||||
return getRegistrationBalance().compareTo(Coin.ZERO) > 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("UnusedDeclaration")
|
|
||||||
public boolean isRegistrationFeeBalanceSufficient() {
|
|
||||||
return getRegistrationBalance().compareTo(FeePolicy.REGISTRATION_FEE) >= 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
//TODO
|
|
||||||
@SuppressWarnings("SameReturnValue")
|
|
||||||
public int getNumOfPeersSeenTx(String txId) {
|
|
||||||
// TODO check from blockchain
|
|
||||||
// will be async
|
|
||||||
return 3;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Transactions
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public void payRegistrationFee(String stringifiedFiatAccounts, FutureCallback<Transaction> callback) throws
|
|
||||||
InsufficientMoneyException {
|
|
||||||
log.debug("payRegistrationFee");
|
|
||||||
log.trace("stringifiedFiatAccounts " + stringifiedFiatAccounts);
|
|
||||||
|
|
||||||
Transaction tx = new Transaction(params);
|
|
||||||
|
|
||||||
byte[] data = cryptoService.digestMessageWithSignature(getRegistrationAddressEntry().getKeyPair(), stringifiedFiatAccounts);
|
|
||||||
tx.addOutput(Transaction.MIN_NONDUST_OUTPUT, new ScriptBuilder().op(OP_RETURN).data(data).build());
|
|
||||||
|
|
||||||
// We don't take a fee at the moment
|
|
||||||
// 0.0000454 BTC will get extra to miners as it is lower then durst
|
|
||||||
/* Coin fee = FeePolicy.REGISTRATION_FEE
|
|
||||||
.subtract(Transaction.MIN_NONDUST_OUTPUT)
|
|
||||||
.subtract(FeePolicy.TX_FEE);
|
|
||||||
log.trace("fee: " + fee.toFriendlyString());
|
|
||||||
tx.addOutput(fee, feePolicy.getAddressForRegistrationFee());*/
|
|
||||||
|
|
||||||
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
|
|
||||||
sendRequest.shuffleOutputs = false;
|
|
||||||
|
|
||||||
// We accept at the moment registration fee payment with 0 confirmations.
|
|
||||||
// The verification will be done at the end of the trade process again, and then a double spend would be
|
|
||||||
// detected and lead to arbitration.
|
|
||||||
// The last param (boolean includePending) is used for indicating that we accept 0 conf tx.
|
|
||||||
sendRequest.coinSelector = new AddressBasedCoinSelector(params, getRegistrationAddressEntry(), true);
|
|
||||||
sendRequest.changeAddress = getRegistrationAddressEntry().getAddress();
|
|
||||||
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
|
||||||
Futures.addCallback(sendResult.broadcastComplete, callback);
|
|
||||||
|
|
||||||
log.debug("Registration transaction: " + tx);
|
|
||||||
printTxWithInputs("payRegistrationFee", tx);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Withdrawal
|
// Withdrawal
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public String sendFunds(String withdrawFromAddress,
|
public String sendFunds(String fromAddress,
|
||||||
String withdrawToAddress,
|
String toAddress,
|
||||||
Coin amount,
|
Coin amount,
|
||||||
FutureCallback<Transaction> callback) throws AddressFormatException, InsufficientMoneyException, IllegalArgumentException {
|
KeyParameter aesKey,
|
||||||
|
FutureCallback<Transaction> callback) throws AddressFormatException, IllegalArgumentException, InsufficientMoneyException {
|
||||||
Transaction tx = new Transaction(params);
|
Transaction tx = new Transaction(params);
|
||||||
tx.addOutput(amount.subtract(FeePolicy.TX_FEE), new Address(params, withdrawToAddress));
|
tx.addOutput(amount.subtract(FeePolicy.TX_FEE), new Address(params, toAddress));
|
||||||
|
|
||||||
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
|
Wallet.SendRequest sendRequest = Wallet.SendRequest.forTx(tx);
|
||||||
|
sendRequest.aesKey = aesKey;
|
||||||
sendRequest.shuffleOutputs = false;
|
sendRequest.shuffleOutputs = false;
|
||||||
// we allow spending of unconfirmed tx (double spend risk is low and usability would suffer if we need to
|
Optional<AddressEntry> addressEntry = getAddressEntryByAddress(fromAddress);
|
||||||
// wait for 1 confirmation)
|
|
||||||
|
|
||||||
Optional<AddressEntry> addressEntry = getAddressEntryByAddressString(withdrawFromAddress);
|
|
||||||
if (!addressEntry.isPresent())
|
if (!addressEntry.isPresent())
|
||||||
throw new IllegalArgumentException("WithdrawFromAddress is not found in our wallets.");
|
throw new IllegalArgumentException("WithdrawFromAddress is not found in our wallets.");
|
||||||
|
|
||||||
sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry.get(), true);
|
sendRequest.coinSelector = new AddressBasedCoinSelector(params, addressEntry.get());
|
||||||
sendRequest.changeAddress = addressEntry.get().getAddress();
|
sendRequest.changeAddress = addressEntry.get().getAddress();
|
||||||
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
||||||
Futures.addCallback(sendResult.broadcastComplete, callback);
|
Futures.addCallback(sendResult.broadcastComplete, callback);
|
||||||
@ -580,6 +444,45 @@ public class WalletService {
|
|||||||
return tx.getHashAsString();
|
return tx.getHashAsString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void emptyWallet(String toAddress, KeyParameter aesKey, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler)
|
||||||
|
throws InsufficientMoneyException, AddressFormatException {
|
||||||
|
Wallet.SendRequest sendRequest = Wallet.SendRequest.emptyWallet(new Address(params, toAddress));
|
||||||
|
sendRequest.aesKey = aesKey;
|
||||||
|
Wallet.SendResult sendResult = wallet.sendCoins(sendRequest);
|
||||||
|
Futures.addCallback(sendResult.broadcastComplete, new FutureCallback<Transaction>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(Transaction result) {
|
||||||
|
resultHandler.handleResult();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFailure(@NotNull Throwable t) {
|
||||||
|
errorMessageHandler.handleErrorMessage(t.getMessage());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Getters
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public ReadOnlyDoubleProperty downloadPercentageProperty() {
|
||||||
|
return downloadListener.percentageProperty();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Wallet getWallet() {
|
||||||
|
return wallet;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Transaction getTransactionFromSerializedTx(byte[] tx) {
|
||||||
|
return new Transaction(params, tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyIntegerProperty numPeersProperty() {
|
||||||
|
return numPeers;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Private methods
|
// Private methods
|
||||||
@ -595,13 +498,6 @@ public class WalletService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getNumPeers() {
|
|
||||||
return numPeers.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyIntegerProperty numPeersProperty() {
|
|
||||||
return numPeers;
|
|
||||||
}
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Inner classes
|
// Inner classes
|
||||||
@ -613,13 +509,13 @@ public class WalletService {
|
|||||||
@Override
|
@Override
|
||||||
protected void progress(double percentage, int blocksLeft, Date date) {
|
protected void progress(double percentage, int blocksLeft, Date date) {
|
||||||
super.progress(percentage, blocksLeft, date);
|
super.progress(percentage, blocksLeft, date);
|
||||||
Threading.USER_THREAD.execute(() -> this.percentage.set(percentage / 100d));
|
UserThread.execute(() -> this.percentage.set(percentage / 100d));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void doneDownload() {
|
protected void doneDownload() {
|
||||||
super.doneDownload();
|
super.doneDownload();
|
||||||
Threading.USER_THREAD.execute(() -> this.percentage.set(1d));
|
UserThread.execute(() -> this.percentage.set(1d));
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyDoubleProperty percentageProperty() {
|
public ReadOnlyDoubleProperty percentageProperty() {
|
||||||
@ -661,10 +557,69 @@ public class WalletService {
|
|||||||
if (balanceListener.getAddress() != null)
|
if (balanceListener.getAddress() != null)
|
||||||
balance = getBalanceForAddress(balanceListener.getAddress());
|
balance = getBalanceForAddress(balanceListener.getAddress());
|
||||||
else
|
else
|
||||||
balance = getWalletBalance();
|
balance = getAvailableBalance();
|
||||||
|
|
||||||
balanceListener.onBalanceChanged(balance);
|
balanceListener.onBalanceChanged(balance);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* // TODO
|
||||||
|
private class BloomFilterForForeignTx extends AbstractPeerEventListener implements PeerFilterProvider {
|
||||||
|
private final String txId;
|
||||||
|
|
||||||
|
public BloomFilterForForeignTx(String txId) {
|
||||||
|
this.txId = txId;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getEarliestKeyCreationTime() {
|
||||||
|
return Utils.currentTimeSeconds();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void beginBloomFilterCalculation() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getBloomFilterElementCount() {
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public BloomFilter getBloomFilter(int size, double falsePositiveRate, long nTweak) {
|
||||||
|
BloomFilter filter = new BloomFilter(size, falsePositiveRate, nTweak);
|
||||||
|
*//* for (TransactionOutPoint pledge : allPledges.keySet()) {
|
||||||
|
filter.insert(pledge.bitcoinSerialize());
|
||||||
|
}*//*
|
||||||
|
// how to add txid ???
|
||||||
|
return filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isRequiringUpdateAllBloomFilter() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void endBloomFilterCalculation() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTransaction(Peer peer, Transaction t) {
|
||||||
|
// executor.checkOnThread();
|
||||||
|
// TODO: Gate this logic on t being announced by multiple peers.
|
||||||
|
// checkForRevocation(t);
|
||||||
|
// TODO: Watch out for the confirmation. If no confirmation of the revocation occurs within N hours, alert the user.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
public void findTxInBlockChain(String txId) {
|
||||||
|
// https://groups.google.com/forum/?hl=de#!topic/bitcoinj/kinFP7lLsRE
|
||||||
|
// https://groups.google.com/forum/?hl=de#!topic/bitcoinj/f7m87kCWdb8
|
||||||
|
// https://groups.google.com/forum/?hl=de#!topic/bitcoinj/jNE5ohLExVM
|
||||||
|
walletAppKit.peerGroup().addPeerFilterProvider(new BloomFilterForForeignTx(txId));
|
||||||
|
}*/
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.common.handlers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For reporting error message only (UI)
|
|
||||||
*/
|
|
||||||
public interface ErrorMessageHandler {
|
|
||||||
void handleErrorMessage(String errorMessage);
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.common.handlers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For reporting throwable objects only
|
|
||||||
*/
|
|
||||||
public interface ExceptionHandler {
|
|
||||||
void handleException(Throwable throwable);
|
|
||||||
}
|
|
@ -1,25 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.common.handlers;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* For reporting a description message and throwable
|
|
||||||
*/
|
|
||||||
public interface FaultHandler {
|
|
||||||
void handleFault(String errorMessage, Throwable throwable);
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.common.handlers;
|
|
||||||
|
|
||||||
public interface ResultHandler {
|
|
||||||
void handleResult();
|
|
||||||
}
|
|
@ -1,30 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.common.taskrunner;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class InterceptTaskException extends RuntimeException {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(InterceptTaskException.class);
|
|
||||||
private static final long serialVersionUID = 5216202440370333534L;
|
|
||||||
|
|
||||||
public InterceptTaskException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.common.taskrunner;
|
|
||||||
|
|
||||||
public interface Model {
|
|
||||||
void persist();
|
|
||||||
|
|
||||||
void onComplete();
|
|
||||||
}
|
|
@ -1,74 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.common.taskrunner;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public abstract class Task<T extends Model> {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(Task.class);
|
|
||||||
|
|
||||||
public static Class<? extends Task> taskToIntercept;
|
|
||||||
|
|
||||||
private final TaskRunner taskHandler;
|
|
||||||
protected final T model;
|
|
||||||
protected String errorMessage = "An error occurred at: " + getClass().getSimpleName();
|
|
||||||
|
|
||||||
public Task(TaskRunner taskHandler, T model) {
|
|
||||||
this.taskHandler = taskHandler;
|
|
||||||
this.model = model;
|
|
||||||
}
|
|
||||||
|
|
||||||
abstract protected void run();
|
|
||||||
|
|
||||||
protected void runInterceptHook() {
|
|
||||||
if (getClass() == taskToIntercept)
|
|
||||||
throw new InterceptTaskException("Task intercepted for testing purpose. Task = " + getClass().getSimpleName());
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void appendToErrorMessage(String message) {
|
|
||||||
errorMessage += "\n" + message;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void appendExceptionToErrorMessage(Throwable t) {
|
|
||||||
if (t.getMessage() != null)
|
|
||||||
errorMessage += "\nException message: " + t.getMessage();
|
|
||||||
else
|
|
||||||
errorMessage += "\nException: " + t.toString();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void complete() {
|
|
||||||
taskHandler.handleComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void failed(String message) {
|
|
||||||
appendToErrorMessage(message);
|
|
||||||
failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void failed(Throwable t) {
|
|
||||||
t.printStackTrace();
|
|
||||||
appendExceptionToErrorMessage(t);
|
|
||||||
failed();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void failed() {
|
|
||||||
taskHandler.handleErrorMessage(errorMessage);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -1,97 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.common.taskrunner;
|
|
||||||
|
|
||||||
import io.bitsquare.common.handlers.ErrorMessageHandler;
|
|
||||||
import io.bitsquare.common.handlers.ResultHandler;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Queue;
|
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class TaskRunner<T extends Model> {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(TaskRunner.class);
|
|
||||||
|
|
||||||
private final Queue<Class<? extends Task>> tasks = new LinkedBlockingQueue<>();
|
|
||||||
protected final T sharedModel;
|
|
||||||
private final Class<T> sharedModelClass;
|
|
||||||
private final ResultHandler resultHandler;
|
|
||||||
private final ErrorMessageHandler errorMessageHandler;
|
|
||||||
private boolean failed = false;
|
|
||||||
private boolean isCanceled;
|
|
||||||
|
|
||||||
private Class<? extends Task> currentTask;
|
|
||||||
|
|
||||||
|
|
||||||
public TaskRunner(T sharedModel, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
this(sharedModel, (Class<T>) sharedModel.getClass(), resultHandler, errorMessageHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaskRunner(T sharedModel, Class<T> sharedModelClass, ResultHandler resultHandler, ErrorMessageHandler errorMessageHandler) {
|
|
||||||
this.sharedModel = sharedModel;
|
|
||||||
this.resultHandler = resultHandler;
|
|
||||||
this.errorMessageHandler = errorMessageHandler;
|
|
||||||
this.sharedModelClass = sharedModelClass;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void addTasks(Class<? extends Task<T>>... items) {
|
|
||||||
tasks.addAll(Arrays.asList(items));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void run() {
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void next() {
|
|
||||||
if (!failed && !isCanceled) {
|
|
||||||
if (tasks.size() > 0) {
|
|
||||||
try {
|
|
||||||
currentTask = tasks.poll();
|
|
||||||
log.trace("Run task: " + currentTask.getSimpleName());
|
|
||||||
currentTask.getDeclaredConstructor(TaskRunner.class, sharedModelClass).newInstance(this, sharedModel).run();
|
|
||||||
} catch (Throwable throwable) {
|
|
||||||
throwable.printStackTrace();
|
|
||||||
handleErrorMessage("Error at taskRunner: " + throwable.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
resultHandler.handleResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void cancel() {
|
|
||||||
isCanceled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleComplete() {
|
|
||||||
log.trace("Task completed: " + currentTask.getSimpleName());
|
|
||||||
sharedModel.persist();
|
|
||||||
next();
|
|
||||||
}
|
|
||||||
|
|
||||||
void handleErrorMessage(String errorMessage) {
|
|
||||||
log.error("Task failed: " + currentTask.getSimpleName());
|
|
||||||
log.error("errorMessage: " + errorMessage);
|
|
||||||
failed = true;
|
|
||||||
errorMessageHandler.handleErrorMessage(errorMessage);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,228 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.crypto;
|
|
||||||
|
|
||||||
import io.bitsquare.p2p.Message;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.ECKey;
|
|
||||||
import org.bitcoinj.core.Sha256Hash;
|
|
||||||
import org.bitcoinj.core.Utils;
|
|
||||||
|
|
||||||
import com.google.common.base.Charsets;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.security.InvalidKeyException;
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.KeyPairGenerator;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.Security;
|
|
||||||
import java.security.Signature;
|
|
||||||
import java.security.SignatureException;
|
|
||||||
import java.security.SignedObject;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import org.spongycastle.util.encoders.Base64;
|
|
||||||
|
|
||||||
import javax.crypto.BadPaddingException;
|
|
||||||
import javax.crypto.Cipher;
|
|
||||||
import javax.crypto.IllegalBlockSizeException;
|
|
||||||
import javax.crypto.KeyGenerator;
|
|
||||||
import javax.crypto.NoSuchPaddingException;
|
|
||||||
import javax.crypto.SealedObject;
|
|
||||||
import javax.crypto.SecretKey;
|
|
||||||
import org.bouncycastle.jce.provider.BouncyCastleProvider;
|
|
||||||
|
|
||||||
public class CryptoService<T> {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(CryptoService.class);
|
|
||||||
public static final String DHT_SIGN_KEY_ALGO = "DSA";
|
|
||||||
public static final String MSG_SIGN_KEY_ALGO = "DSA";
|
|
||||||
public static final String MSG_ENCR_KEY_ALGO = "RSA";
|
|
||||||
|
|
||||||
private static final String SYM_ENCR_KEY_ALGO = "AES";
|
|
||||||
private static final String SYM_CIPHER = "AES";
|
|
||||||
private static final String ASYM_CIPHER = "RSA"; //RSA/ECB/PKCS1Padding
|
|
||||||
private static final String MSG_SIGN_ALGO = "SHA1withDSA";
|
|
||||||
|
|
||||||
public static KeyPair generateDhtSignatureKeyPair() throws NoSuchAlgorithmException {
|
|
||||||
long ts = System.currentTimeMillis();
|
|
||||||
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(DHT_SIGN_KEY_ALGO);
|
|
||||||
keyPairGenerator.initialize(1024);
|
|
||||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
|
||||||
log.debug("Generate dhtSignatureKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
|
||||||
return keyPair;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static KeyPair generateMsgSignatureKeyPair() throws NoSuchAlgorithmException {
|
|
||||||
long ts = System.currentTimeMillis();
|
|
||||||
final KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(MSG_SIGN_KEY_ALGO);
|
|
||||||
keyPairGenerator.initialize(1024);
|
|
||||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
|
||||||
log.debug("Generate dhtSignatureKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
|
||||||
return keyPair;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static KeyPair generateMsgEncryptionKeyPair() throws NoSuchAlgorithmException {
|
|
||||||
long ts = System.currentTimeMillis();
|
|
||||||
KeyPairGenerator keyPairGenerator = KeyPairGenerator.getInstance(MSG_ENCR_KEY_ALGO);
|
|
||||||
keyPairGenerator.initialize(2048);
|
|
||||||
KeyPair keyPair = keyPairGenerator.genKeyPair();
|
|
||||||
log.debug("Generate msgEncryptionKeyPair needed {} ms", System.currentTimeMillis() - ts);
|
|
||||||
return keyPair;
|
|
||||||
}
|
|
||||||
|
|
||||||
static {
|
|
||||||
Security.addProvider(new BouncyCastleProvider());
|
|
||||||
}
|
|
||||||
|
|
||||||
private KeyRing keyRing;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public CryptoService(KeyRing keyRing) {
|
|
||||||
this.keyRing = keyRing;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SealedAndSignedMessage encryptAndSignMessage(PubKeyRing pubKeyRing, Message message) throws CryptoException {
|
|
||||||
long ts = System.currentTimeMillis();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Create symmetric key
|
|
||||||
KeyGenerator keyGenerator = KeyGenerator.getInstance(SYM_ENCR_KEY_ALGO);
|
|
||||||
keyGenerator.init(128);
|
|
||||||
SecretKey secretKey = keyGenerator.generateKey();
|
|
||||||
|
|
||||||
// Encrypt secretKey with peers pubKey using SealedObject
|
|
||||||
Cipher cipherAsym = Cipher.getInstance(ASYM_CIPHER);
|
|
||||||
cipherAsym.init(Cipher.ENCRYPT_MODE, pubKeyRing.getMsgEncryptionPubKey());
|
|
||||||
SealedObject sealedSecretKey = new SealedObject(secretKey, cipherAsym);
|
|
||||||
|
|
||||||
// Sign (hash of) message and pack it into SignedObject
|
|
||||||
SignedObject signedMessage = new SignedObject(message, keyRing.getMsgSignatureKeyPair().getPrivate(), Signature.getInstance(MSG_SIGN_ALGO));
|
|
||||||
|
|
||||||
// // Encrypt signedMessage with secretKey using SealedObject
|
|
||||||
Cipher cipherSym = Cipher.getInstance(SYM_CIPHER);
|
|
||||||
cipherSym.init(Cipher.ENCRYPT_MODE, secretKey);
|
|
||||||
SealedObject sealedMessage = new SealedObject(signedMessage, cipherSym);
|
|
||||||
|
|
||||||
SealedAndSignedMessage sealedAndSignedMessage = new SealedAndSignedMessage(sealedSecretKey,
|
|
||||||
sealedMessage,
|
|
||||||
keyRing.getMsgSignatureKeyPair().getPublic()
|
|
||||||
);
|
|
||||||
log.debug("Encryption needed {} ms", System.currentTimeMillis() - ts);
|
|
||||||
return sealedAndSignedMessage;
|
|
||||||
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException
|
|
||||||
| IllegalBlockSizeException | IOException | SignatureException e) {
|
|
||||||
throw new CryptoException(e);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public MessageWithPubKey decryptAndVerifyMessage(SealedAndSignedMessage sealedAndSignedMessage) throws CryptoException {
|
|
||||||
long ts = System.currentTimeMillis();
|
|
||||||
try {
|
|
||||||
SealedObject sealedSecretKey = sealedAndSignedMessage.getSealedSecretKey();
|
|
||||||
SealedObject sealedMessage = sealedAndSignedMessage.getSealedMessage();
|
|
||||||
PublicKey signaturePubKey = sealedAndSignedMessage.getSignaturePubKey();
|
|
||||||
|
|
||||||
// Decrypt secretKey with my privKey
|
|
||||||
Cipher cipherAsym = Cipher.getInstance(ASYM_CIPHER);
|
|
||||||
cipherAsym.init(Cipher.DECRYPT_MODE, keyRing.getMsgEncryptionKeyPair().getPrivate());
|
|
||||||
Object secretKeyObject = sealedSecretKey.getObject(cipherAsym);
|
|
||||||
if (secretKeyObject instanceof SecretKey) {
|
|
||||||
SecretKey secretKey = (SecretKey) secretKeyObject;
|
|
||||||
|
|
||||||
// Decrypt signedMessage with secretKey
|
|
||||||
Cipher cipherSym = Cipher.getInstance(SYM_CIPHER);
|
|
||||||
cipherSym.init(Cipher.DECRYPT_MODE, secretKey);
|
|
||||||
Object signedMessageObject = sealedMessage.getObject(cipherSym);
|
|
||||||
if (signedMessageObject instanceof SignedObject) {
|
|
||||||
SignedObject signedMessage = (SignedObject) signedMessageObject;
|
|
||||||
|
|
||||||
// Verify message with peers pubKey
|
|
||||||
if (signedMessage.verify(signaturePubKey, Signature.getInstance(MSG_SIGN_ALGO))) {
|
|
||||||
// Get message
|
|
||||||
Object messageObject = signedMessage.getObject();
|
|
||||||
if (messageObject instanceof Message) {
|
|
||||||
log.debug("Decryption needed {} ms", System.currentTimeMillis() - ts);
|
|
||||||
return new MessageWithPubKey((Message) messageObject, signaturePubKey);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new CryptoException("messageObject is not instance of Message");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new CryptoException("Signature is not valid");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new CryptoException("signedMessageObject is not instance of SignedObject");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new CryptoException("secretKeyObject is not instance of SecretKey");
|
|
||||||
}
|
|
||||||
} catch (NoSuchPaddingException | NoSuchAlgorithmException | InvalidKeyException | BadPaddingException |
|
|
||||||
ClassNotFoundException | IllegalBlockSizeException | IOException | SignatureException e) {
|
|
||||||
throw new CryptoException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public String signMessage(ECKey key, Sha256Hash hash) {
|
|
||||||
ECKey.ECDSASignature sig = key.sign(hash, null);
|
|
||||||
// Now we have to work backwards to figure out the recId needed to recover the signature.
|
|
||||||
int recId = -1;
|
|
||||||
for (int i = 0; i < 4; i++) {
|
|
||||||
ECKey k = ECKey.recoverFromSignature(i, sig, hash, key.isCompressed());
|
|
||||||
if (k != null && k.getPubKeyPoint().equals(key.getPubKeyPoint())) {
|
|
||||||
recId = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (recId == -1)
|
|
||||||
throw new RuntimeException("Could not construct a recoverable key. This should never happen.");
|
|
||||||
int headerByte = recId + 27 + (key.isCompressed() ? 4 : 0);
|
|
||||||
byte[] sigData = new byte[65]; // 1 header + 32 bytes for R + 32 bytes for S
|
|
||||||
sigData[0] = (byte) headerByte;
|
|
||||||
System.arraycopy(Utils.bigIntegerToBytes(sig.r, 32), 0, sigData, 1, 32);
|
|
||||||
System.arraycopy(Utils.bigIntegerToBytes(sig.s, 32), 0, sigData, 33, 32);
|
|
||||||
return new String(Base64.encode(sigData), Charsets.UTF_8);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public byte[] digestMessageWithSignature(ECKey key, String message) {
|
|
||||||
String signedMessage = signMessage(key, message);
|
|
||||||
return Utils.sha256hash160(message.concat(signedMessage).getBytes(Charsets.UTF_8));
|
|
||||||
}
|
|
||||||
|
|
||||||
public String signMessage(ECKey key, String message) {
|
|
||||||
byte[] data = Utils.formatMessageForSigning(message);
|
|
||||||
Sha256Hash hash = Sha256Hash.hashTwice(data);
|
|
||||||
return signMessage(key, hash);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Sha256Hash hash(String message) {
|
|
||||||
byte[] data = Utils.formatMessageForSigning(message);
|
|
||||||
return Sha256Hash.hashTwice(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
@ -1,46 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.crypto;
|
|
||||||
|
|
||||||
import io.bitsquare.p2p.Message;
|
|
||||||
|
|
||||||
import java.security.PublicKey;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class MessageWithPubKey implements Message {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(MessageWithPubKey.class);
|
|
||||||
|
|
||||||
public Message getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PublicKey getSignaturePubKey() {
|
|
||||||
return signaturePubKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
private final Message message;
|
|
||||||
private final PublicKey signaturePubKey;
|
|
||||||
|
|
||||||
|
|
||||||
public MessageWithPubKey(Message message, PublicKey signaturePubKey) {
|
|
||||||
this.message = message;
|
|
||||||
this.signaturePubKey = signaturePubKey;
|
|
||||||
}
|
|
||||||
}
|
|
40
core/src/main/java/io/bitsquare/crypto/ScryptUtil.java
Normal file
40
core/src/main/java/io/bitsquare/crypto/ScryptUtil.java
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
package io.bitsquare.crypto;
|
||||||
|
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
import io.bitsquare.common.UserThread;
|
||||||
|
import org.bitcoinj.crypto.KeyCrypterScrypt;
|
||||||
|
import org.bitcoinj.wallet.Protos;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
|
//TODO
|
||||||
|
public class ScryptUtil {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(ScryptUtil.class);
|
||||||
|
|
||||||
|
public ScryptUtil() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Protos.ScryptParameters SCRYPT_PARAMETERS = Protos.ScryptParameters.newBuilder()
|
||||||
|
.setP(6)
|
||||||
|
.setR(8)
|
||||||
|
.setN(32768)
|
||||||
|
.setSalt(ByteString.copyFrom(KeyCrypterScrypt.randomSalt()))
|
||||||
|
.build();
|
||||||
|
|
||||||
|
public interface ScryptKeyDerivationResultHandler {
|
||||||
|
void handleResult(KeyParameter aesKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void deriveKeyWithScrypt(KeyCrypterScrypt keyCrypterScrypt, String password, ScryptKeyDerivationResultHandler resultHandler) {
|
||||||
|
new Thread(() -> {
|
||||||
|
log.info("Doing key derivation");
|
||||||
|
|
||||||
|
long start = System.currentTimeMillis();
|
||||||
|
KeyParameter aesKey = keyCrypterScrypt.deriveKey(password);
|
||||||
|
long duration = System.currentTimeMillis() - start;
|
||||||
|
log.info("Key derivation took {} msec", duration);
|
||||||
|
UserThread.execute(() -> resultHandler.handleResult(aesKey));
|
||||||
|
}).start();
|
||||||
|
}
|
||||||
|
}
|
@ -1,64 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.crypto;
|
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
|
||||||
import io.bitsquare.p2p.Message;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import java.security.PublicKey;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import javax.crypto.SealedObject;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Packs the encrypted symmetric secretKey and the encrypted signed message into one object.
|
|
||||||
* SecretKey is encrypted with asymmetric pubKey of peer. Signed message is encrypted with secretKey.
|
|
||||||
* Using that hybrid encryption model we are not restricted by data size and performance as symmetric encryption is very fast.
|
|
||||||
*/
|
|
||||||
public class SealedAndSignedMessage implements Serializable, Message {
|
|
||||||
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
|
||||||
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
|
|
||||||
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(SealedAndSignedMessage.class);
|
|
||||||
|
|
||||||
private final SealedObject sealedSecretKey;
|
|
||||||
private final SealedObject sealedMessage;
|
|
||||||
private final PublicKey signaturePubKey;
|
|
||||||
|
|
||||||
public SealedAndSignedMessage(SealedObject sealedSecretKey, SealedObject sealedMessage, PublicKey signaturePubKey) {
|
|
||||||
this.sealedSecretKey = sealedSecretKey;
|
|
||||||
this.sealedMessage = sealedMessage;
|
|
||||||
this.signaturePubKey = signaturePubKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SealedObject getSealedSecretKey() {
|
|
||||||
return sealedSecretKey;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SealedObject getSealedMessage() {
|
|
||||||
return sealedMessage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PublicKey getSignaturePubKey() {
|
|
||||||
return signaturePubKey;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,47 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.crypto;
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Utils;
|
|
||||||
|
|
||||||
import java.security.KeyFactory;
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
import java.security.spec.InvalidKeySpecException;
|
|
||||||
import java.security.spec.X509EncodedKeySpec;
|
|
||||||
|
|
||||||
import java.util.Base64;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class Util {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(Util.class);
|
|
||||||
|
|
||||||
public static String pubKeyToString(PublicKey publicKey) {
|
|
||||||
final X509EncodedKeySpec x509EncodedKeySpec = new X509EncodedKeySpec(publicKey.getEncoded());
|
|
||||||
return Base64.getEncoder().encodeToString(x509EncodedKeySpec.getEncoded());
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO just temp for arbitrator
|
|
||||||
public static PublicKey decodeDSAPubKeyHex(String pubKeyHex) throws NoSuchAlgorithmException, InvalidKeySpecException {
|
|
||||||
X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(Utils.HEX.decode(pubKeyHex));
|
|
||||||
KeyFactory keyFactory = KeyFactory.getInstance("DSA");
|
|
||||||
return keyFactory.generatePublic(pubKeySpec);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,128 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.fiat;
|
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
|
||||||
import io.bitsquare.locale.Country;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Objects;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
@Immutable
|
|
||||||
public class FiatAccount implements Serializable {
|
|
||||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
|
||||||
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
|
|
||||||
|
|
||||||
public static final long HOUR_IN_BLOCKS = 6;
|
|
||||||
public static final long DAY_IN_BLOCKS = HOUR_IN_BLOCKS * 24;
|
|
||||||
public static final long WEEK_IN_BLOCKS = DAY_IN_BLOCKS * 7;
|
|
||||||
|
|
||||||
public enum Type {
|
|
||||||
IRC("", "", 0),
|
|
||||||
SEPA("IBAN", "BIC", WEEK_IN_BLOCKS),
|
|
||||||
WIRE("primary ID", "secondary ID", WEEK_IN_BLOCKS),
|
|
||||||
INTERNATIONAL("primary ID", "secondary ID", 2 * WEEK_IN_BLOCKS),
|
|
||||||
OK_PAY("primary ID", "secondary ID", HOUR_IN_BLOCKS),
|
|
||||||
NET_TELLER("primary ID", "secondary ID", HOUR_IN_BLOCKS),
|
|
||||||
PERFECT_MONEY("primary ID", "secondary ID", HOUR_IN_BLOCKS);
|
|
||||||
|
|
||||||
public final String primaryId;
|
|
||||||
public final String secondaryId;
|
|
||||||
public final long lockTimeDelta;
|
|
||||||
|
|
||||||
Type(String primaryId, String secondaryId, long lockTimeDelta) {
|
|
||||||
this.primaryId = primaryId;
|
|
||||||
this.secondaryId = secondaryId;
|
|
||||||
this.lockTimeDelta = lockTimeDelta;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ArrayList<Type> getAllBankAccountTypes() {
|
|
||||||
return new ArrayList<>(Arrays.asList(Type.values()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public final String id;
|
|
||||||
public final String nameOfBank;
|
|
||||||
public final Type type;
|
|
||||||
public final Country country; // where bank is registered
|
|
||||||
public final String accountPrimaryID; // like IBAN
|
|
||||||
public final String accountSecondaryID; // like BIC
|
|
||||||
public final String accountHolderName;
|
|
||||||
|
|
||||||
|
|
||||||
// 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.
|
|
||||||
public final String currencyCode;
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Constructor
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public FiatAccount(Type type, String currencyCode, Country country, String nameOfBank,
|
|
||||||
String accountHolderName, String accountPrimaryID, String accountSecondaryID) {
|
|
||||||
this.type = type;
|
|
||||||
this.currencyCode = currencyCode;
|
|
||||||
this.country = country;
|
|
||||||
this.nameOfBank = nameOfBank;
|
|
||||||
this.accountHolderName = accountHolderName;
|
|
||||||
this.accountPrimaryID = accountPrimaryID;
|
|
||||||
this.accountSecondaryID = accountSecondaryID;
|
|
||||||
|
|
||||||
id = nameOfBank;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hashCode(id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object obj) {
|
|
||||||
if (!(obj instanceof FiatAccount)) return false;
|
|
||||||
if (obj == this) return true;
|
|
||||||
|
|
||||||
final FiatAccount other = (FiatAccount) obj;
|
|
||||||
return id.equals(other.id);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "FiatAccount{" +
|
|
||||||
"id='" + id + '\'' +
|
|
||||||
", nameOfBank='" + nameOfBank + '\'' +
|
|
||||||
", type=" + type +
|
|
||||||
", country=" + country +
|
|
||||||
", accountPrimaryID='" + accountPrimaryID + '\'' +
|
|
||||||
", accountSecondaryID='" + accountSecondaryID + '\'' +
|
|
||||||
", accountHolderName='" + accountHolderName + '\'' +
|
|
||||||
", currencyCode='" + currencyCode + '\'' +
|
|
||||||
'}';
|
|
||||||
}
|
|
||||||
}
|
|
@ -17,23 +17,21 @@
|
|||||||
|
|
||||||
package io.bitsquare.locale;
|
package io.bitsquare.locale;
|
||||||
|
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.net.URLConnection;
|
import java.net.URLConnection;
|
||||||
|
|
||||||
import java.text.MessageFormat;
|
import java.text.MessageFormat;
|
||||||
|
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.MissingResourceException;
|
import java.util.MissingResourceException;
|
||||||
import java.util.PropertyResourceBundle;
|
import java.util.PropertyResourceBundle;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class BSResources {
|
public class BSResources {
|
||||||
private static final Logger log = LoggerFactory.getLogger(BSResources.class);
|
private static final Logger log = LoggerFactory.getLogger(BSResources.class);
|
||||||
|
|
||||||
@ -61,7 +59,7 @@ public class BSResources {
|
|||||||
// Adds UTF8 support for property files
|
// Adds UTF8 support for property files
|
||||||
class UTF8Control extends ResourceBundle.Control {
|
class UTF8Control extends ResourceBundle.Control {
|
||||||
|
|
||||||
public ResourceBundle newBundle(String baseName, Locale locale, String format, ClassLoader loader, boolean reload)
|
public ResourceBundle newBundle(String baseName, @NotNull Locale locale, @NotNull String format, ClassLoader loader, boolean reload)
|
||||||
throws IllegalAccessException, InstantiationException, IOException {
|
throws IllegalAccessException, InstantiationException, IOException {
|
||||||
// The below is a copy of the default implementation.
|
// The below is a copy of the default implementation.
|
||||||
final String bundleName = toBundleName(baseName, locale);
|
final String bundleName = toBundleName(baseName, locale);
|
||||||
@ -77,8 +75,7 @@ class UTF8Control extends ResourceBundle.Control {
|
|||||||
stream = connection.getInputStream();
|
stream = connection.getInputStream();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
stream = loader.getResourceAsStream(resourceName);
|
stream = loader.getResourceAsStream(resourceName);
|
||||||
}
|
}
|
||||||
if (stream != null) {
|
if (stream != null) {
|
||||||
|
@ -19,14 +19,13 @@ package io.bitsquare.locale;
|
|||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
public class Country implements Serializable {
|
public class Country implements Serializable {
|
||||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
||||||
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
|
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
|
||||||
|
|
||||||
public final String code;
|
public final String code;
|
||||||
public final String name;
|
public final String name;
|
||||||
|
@ -19,16 +19,12 @@ package io.bitsquare.locale;
|
|||||||
|
|
||||||
import com.google.common.collect.Collections2;
|
import com.google.common.collect.Collections2;
|
||||||
import com.google.common.collect.Lists;
|
import com.google.common.collect.Lists;
|
||||||
|
import io.bitsquare.user.Preferences;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.*;
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Set;
|
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class CountryUtil {
|
public class CountryUtil {
|
||||||
|
|
||||||
public static List<Region> getAllRegions() {
|
public static List<Region> getAllRegions() {
|
||||||
final List<Region> allRegions = new ArrayList<>();
|
final List<Region> allRegions = new ArrayList<>();
|
||||||
|
|
||||||
@ -59,19 +55,62 @@ public class CountryUtil {
|
|||||||
return allRegions;
|
return allRegions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Country> getAllEuroCountries() {
|
public static List<Country> getAllSepaEuroCountries() {
|
||||||
List<Country> allEuroCountries = new ArrayList<>();
|
List<Country> list = new ArrayList<>();
|
||||||
String[] code = {"BE", "DE", "EE", "FI", "FR", "GR", "IE", "IT", "LV", "LU", "MT", "NL", "PT", "SK", "SI",
|
String[] codes = {"AT", "BE", "CY", "DE", "EE", "FI", "FR", "GR", "IE",
|
||||||
"ES", "AT", "CY"};
|
"IT", "LV", "LT", "LU", "MC", "MT", "NL", "PT", "SK", "SI", "ES"};
|
||||||
for (String aCode : code) {
|
for (String code : codes) {
|
||||||
Locale locale = new Locale("", aCode, "");
|
Locale locale = new Locale(LanguageUtil.getDefaultLanguage(), code, "");
|
||||||
String regionCode = getRegionCode(locale.getCountry());
|
String regionCode = getRegionCode(locale.getCountry());
|
||||||
final Region region = new Region(regionCode, getRegionName(regionCode));
|
final Region region = new Region(regionCode, getRegionName(regionCode));
|
||||||
final Country country = new Country(locale.getCountry(), locale.getDisplayCountry(), region);
|
final Country country = new Country(locale.getCountry(), locale.getDisplayCountry(), region);
|
||||||
allEuroCountries.add(country);
|
list.add(country);
|
||||||
}
|
}
|
||||||
|
list.sort((a, b) -> a.code.compareTo(b.code));
|
||||||
|
|
||||||
return allEuroCountries;
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean containsAllSepaEuroCountries(List<String> countryCodesToCompare) {
|
||||||
|
countryCodesToCompare.sort((a, b) -> a.compareTo(b));
|
||||||
|
List<String> countryCodesBase = getAllSepaEuroCountries().stream().map(c -> c.code).collect(Collectors.toList());
|
||||||
|
return countryCodesToCompare.toString().equals(countryCodesBase.toString());
|
||||||
|
/*
|
||||||
|
List<Country> countriesBase = getAllSepaEuroCountries();
|
||||||
|
List<Country> remainingBase = new ArrayList<>(countriesBase);
|
||||||
|
List<String> remainingToCompare = new ArrayList<>(countryCodesToCompare);
|
||||||
|
for (int i = 0; i < countriesBase.size(); i++) {
|
||||||
|
String countryCodeBase = countriesBase.get(i).code;
|
||||||
|
for (int n = 0; n < countryCodesToCompare.size(); n++) {
|
||||||
|
if (countryCodeBase.equals(countryCodesToCompare.get(n))) {
|
||||||
|
if (remainingBase.size() > 0) remainingBase.remove(i);
|
||||||
|
if (remainingToCompare.size() > 0) remainingToCompare.remove(n);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return remainingBase.size() == 0 && remainingBase.size() == remainingToCompare.size();*/
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Country> getAllSepaNonEuroCountries() {
|
||||||
|
List<Country> list = new ArrayList<>();
|
||||||
|
String[] codes = {"BG", "HR", "CZ", "DK", "GB", "HU", "PL", "RO",
|
||||||
|
"SE", "IS", "NO", "LI", "CH"};
|
||||||
|
for (String code : codes) {
|
||||||
|
Locale locale = new Locale(LanguageUtil.getDefaultLanguage(), code, "");
|
||||||
|
String regionCode = getRegionCode(locale.getCountry());
|
||||||
|
final Region region = new Region(regionCode, getRegionName(regionCode));
|
||||||
|
final Country country = new Country(locale.getCountry(), locale.getDisplayCountry(), region);
|
||||||
|
list.add(country);
|
||||||
|
}
|
||||||
|
list.sort((a, b) -> a.code.compareTo(b.code));
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<Country> getAllSepaCountries() {
|
||||||
|
List<Country> list = new ArrayList<>();
|
||||||
|
list.addAll(getAllSepaEuroCountries());
|
||||||
|
list.addAll(getAllSepaNonEuroCountries());
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<Country> getAllCountriesFor(Region selectedRegion) {
|
public static List<Country> getAllCountriesFor(Region selectedRegion) {
|
||||||
@ -80,7 +119,7 @@ public class CountryUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static Country getDefaultCountry() {
|
public static Country getDefaultCountry() {
|
||||||
final Locale locale = new Locale("", Locale.getDefault().getCountry());
|
final Locale locale = new Locale(LanguageUtil.getDefaultLanguage(), getDefaultCountryCode());
|
||||||
String regionCode = getRegionCode(locale.getCountry());
|
String regionCode = getRegionCode(locale.getCountry());
|
||||||
final Region region = new Region(regionCode, getRegionName(regionCode));
|
final Region region = new Region(regionCode, getRegionName(regionCode));
|
||||||
return new Country(locale.getCountry(), locale.getDisplayCountry(), region);
|
return new Country(locale.getCountry(), locale.getDisplayCountry(), region);
|
||||||
@ -98,6 +137,22 @@ public class CountryUtil {
|
|||||||
return allCountries;
|
return allCountries;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getNameByCode(String countryCode) {
|
||||||
|
return new Locale(LanguageUtil.getDefaultLanguage(), countryCode).getDisplayCountry();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static List<String> getNamesByCodes(List<String> countryCodes) {
|
||||||
|
return countryCodes.stream().map(c -> getNameByCode(c)).collect(Collectors.toList());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getCodesString(List<String> countryCodes) {
|
||||||
|
return countryCodes.stream().collect(Collectors.joining(", "));
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getNamesByCodesString(List<String> countryCodes) {
|
||||||
|
return getNamesByCodes(countryCodes).stream().collect(Collectors.joining(", "));
|
||||||
|
}
|
||||||
|
|
||||||
private static final String[] countryCodes = new String[]{"AE", "AL", "AR", "AT", "AU", "BA", "BE", "BG", "BH",
|
private static final String[] countryCodes = new String[]{"AE", "AL", "AR", "AT", "AU", "BA", "BE", "BG", "BH",
|
||||||
"BO", "BR", "BY", "CA", "CH", "CL", "CN", "CO", "CR", "CS", "CU", "CY", "CZ", "DE", "DK", "DO", "DZ",
|
"BO", "BR", "BY", "CA", "CH", "CL", "CN", "CO", "CR", "CS", "CU", "CY", "CZ", "DE", "DK", "DO", "DZ",
|
||||||
"EC", "EE", "EG", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HR", "HU", "ID", "IE", "IL", "IN",
|
"EC", "EE", "EG", "ES", "FI", "FR", "GB", "GR", "GT", "HK", "HN", "HR", "HU", "ID", "IE", "IL", "IN",
|
||||||
@ -133,7 +188,7 @@ public class CountryUtil {
|
|||||||
private static List<Locale> getAllCountryLocales() {
|
private static List<Locale> getAllCountryLocales() {
|
||||||
List<Locale> allLocales = Arrays.asList(Locale.getAvailableLocales());
|
List<Locale> allLocales = Arrays.asList(Locale.getAvailableLocales());
|
||||||
Set<Locale> allLocalesAsSet = allLocales.stream().filter(locale -> !"".equals(locale.getCountry()))
|
Set<Locale> allLocalesAsSet = allLocales.stream().filter(locale -> !"".equals(locale.getCountry()))
|
||||||
.map(locale -> new Locale("", locale.getCountry(), ""))
|
.map(locale -> new Locale(LanguageUtil.getDefaultLanguage(), locale.getCountry(), ""))
|
||||||
.collect(Collectors.toSet());
|
.collect(Collectors.toSet());
|
||||||
|
|
||||||
allLocales = new ArrayList<>();
|
allLocales = new ArrayList<>();
|
||||||
@ -145,10 +200,14 @@ public class CountryUtil {
|
|||||||
private static String getRegionCode(String countryCode) {
|
private static String getRegionCode(String countryCode) {
|
||||||
if (!countryCode.isEmpty() && countryCodeList.contains(countryCode)) {
|
if (!countryCode.isEmpty() && countryCodeList.contains(countryCode)) {
|
||||||
return regionCodeList.get(countryCodeList.indexOf(countryCode));
|
return regionCodeList.get(countryCodeList.indexOf(countryCode));
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
return "Undefined";
|
return "Undefined";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getDefaultCountryCode() {
|
||||||
|
// might be set later in pref or config, so not use Preferences.getDefaultLocale() anywhere in the code
|
||||||
|
return Preferences.getDefaultLocale().getCountry();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,59 +17,196 @@
|
|||||||
|
|
||||||
package io.bitsquare.locale;
|
package io.bitsquare.locale;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import io.bitsquare.user.Preferences;
|
||||||
import java.util.Currency;
|
import org.slf4j.Logger;
|
||||||
import java.util.List;
|
import org.slf4j.LoggerFactory;
|
||||||
import java.util.Set;
|
|
||||||
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class CurrencyUtil {
|
public class CurrencyUtil {
|
||||||
|
transient private static final Logger log = LoggerFactory.getLogger(CurrencyUtil.class);
|
||||||
|
|
||||||
public static List<String> getAllCurrencyCodes() {
|
private static List<TradeCurrency> allSortedCurrencies = createAllSortedCurrenciesList();
|
||||||
return getAllCurrencies().stream().map(Currency::getCurrencyCode).collect(Collectors.toList());
|
|
||||||
|
public static List<TradeCurrency> getAllSortedCurrencies() {
|
||||||
|
return allSortedCurrencies;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getDefaultCurrencyAsCode() {
|
// We add all currencies supported by payment methods
|
||||||
return getDefaultCurrency().getCurrencyCode();
|
private static List<TradeCurrency> createAllSortedCurrenciesList() {
|
||||||
|
Set<TradeCurrency> set = new HashSet<>();
|
||||||
|
|
||||||
|
// Sepa: EUR at first place
|
||||||
|
set.addAll(getSortedSepaCurrencyCodes());
|
||||||
|
|
||||||
|
// PerfectMoney:
|
||||||
|
set.add(new TradeCurrency("USD"));
|
||||||
|
|
||||||
|
// Alipay:
|
||||||
|
set.add(new TradeCurrency("CNY"));
|
||||||
|
|
||||||
|
// OKPay: We want to maintain the order so we don't use a Set but add items if nto already in list
|
||||||
|
getAllOKPayCurrencies().stream().forEach(e -> set.add(e));
|
||||||
|
|
||||||
|
// Swish: it is already added by Sepa
|
||||||
|
|
||||||
|
// for printing out all codes
|
||||||
|
/* String res;
|
||||||
|
result.stream().forEach(e -> {
|
||||||
|
res += "list.add(new FiatCurrency(\""+e.code+"\"));\n";
|
||||||
|
});
|
||||||
|
log.debug(res);*/
|
||||||
|
|
||||||
|
|
||||||
|
List<TradeCurrency> list = getAllManuallySortedFiatCurrencies();
|
||||||
|
|
||||||
|
// check if the list derived form the payment methods is containing exactly the same as our manually sorted one
|
||||||
|
|
||||||
|
List<String> list1 = set.stream().map(e -> e.code).collect(Collectors.toList());
|
||||||
|
list1.sort((a, b) -> a.compareTo(b));
|
||||||
|
List<String> list2 = list.stream().map(e -> e.code).collect(Collectors.toList());
|
||||||
|
list2.sort((a, b) -> a.compareTo(b));
|
||||||
|
|
||||||
|
if (list1.size() != list2.size()) {
|
||||||
|
log.error("manually defined currencies are not matching currencies derived form our payment methods");
|
||||||
|
log.error("list1 " + list1.toString());
|
||||||
|
log.error("list2 " + list2.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!list1.toString().equals(list2.toString())) {
|
||||||
|
log.error("List derived form the payment methods is not matching exactly the same as our manually sorted one");
|
||||||
|
log.error("list1 " + list1.toString());
|
||||||
|
log.error("list2 " + list2.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Blockchain
|
||||||
|
getSortedCryptoCurrencies().stream().forEach(e -> list.add(e));
|
||||||
|
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getDisplayName(String currencyCode) {
|
private static List<TradeCurrency> getAllManuallySortedFiatCurrencies() {
|
||||||
return Currency.getInstance(currencyCode).getDisplayName();
|
List<TradeCurrency> list = new ArrayList<>();
|
||||||
|
list.add(new FiatCurrency("EUR"));
|
||||||
|
list.add(new FiatCurrency("USD"));
|
||||||
|
list.add(new FiatCurrency("GBP"));
|
||||||
|
list.add(new FiatCurrency("CNY"));
|
||||||
|
list.add(new FiatCurrency("HKD"));
|
||||||
|
list.add(new FiatCurrency("CHF"));
|
||||||
|
list.add(new FiatCurrency("JPY"));
|
||||||
|
list.add(new FiatCurrency("CAD"));
|
||||||
|
list.add(new FiatCurrency("AUD"));
|
||||||
|
list.add(new FiatCurrency("NZD"));
|
||||||
|
list.add(new FiatCurrency("ZAR"));
|
||||||
|
list.add(new FiatCurrency("RUB"));
|
||||||
|
|
||||||
|
list.add(new FiatCurrency("SEK"));
|
||||||
|
list.add(new FiatCurrency("NOK"));
|
||||||
|
list.add(new FiatCurrency("DKK"));
|
||||||
|
list.add(new FiatCurrency("ISK"));
|
||||||
|
|
||||||
|
list.add(new FiatCurrency("PLN"));
|
||||||
|
list.add(new FiatCurrency("CZK"));
|
||||||
|
list.add(new FiatCurrency("TRY"));
|
||||||
|
|
||||||
|
list.add(new FiatCurrency("BGN"));
|
||||||
|
list.add(new FiatCurrency("HRK"));
|
||||||
|
list.add(new FiatCurrency("HUF"));
|
||||||
|
list.add(new FiatCurrency("RON"));
|
||||||
|
|
||||||
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<Currency> getAllCurrencies() {
|
/**
|
||||||
final ArrayList<Currency> mainCurrencies = new ArrayList<>();
|
* @return Sorted list of sepa currencies with EUR as first item
|
||||||
mainCurrencies.add(Currency.getInstance("EUR"));
|
*/
|
||||||
mainCurrencies.add(Currency.getInstance("USD"));
|
public static Set<TradeCurrency> getSortedSepaCurrencyCodes() {
|
||||||
mainCurrencies.add(Currency.getInstance("CNY"));
|
return CountryUtil.getAllSepaCountries().stream()
|
||||||
mainCurrencies.add(Currency.getInstance("RUB"));
|
.map(country -> getCurrencyByCountryCode(country.code))
|
||||||
mainCurrencies.add(Currency.getInstance("JPY"));
|
.collect(Collectors.toSet());
|
||||||
mainCurrencies.add(Currency.getInstance("GBP"));
|
|
||||||
mainCurrencies.add(Currency.getInstance("CAD"));
|
|
||||||
mainCurrencies.add(Currency.getInstance("AUD"));
|
|
||||||
mainCurrencies.add(Currency.getInstance("CHF"));
|
|
||||||
mainCurrencies.add(Currency.getInstance("CNY"));
|
|
||||||
|
|
||||||
Set<Currency> allCurrenciesSet = Currency.getAvailableCurrencies();
|
|
||||||
|
|
||||||
allCurrenciesSet.removeAll(mainCurrencies);
|
|
||||||
final List<Currency> allCurrenciesList = new ArrayList<>(allCurrenciesSet);
|
|
||||||
allCurrenciesList.sort((a, b) -> a.getCurrencyCode().compareTo(b.getCurrencyCode()));
|
|
||||||
|
|
||||||
final List<Currency> resultList = new ArrayList<>(mainCurrencies);
|
|
||||||
resultList.addAll(allCurrenciesList);
|
|
||||||
|
|
||||||
resultList.remove(getDefaultCurrency());
|
|
||||||
resultList.add(0, getDefaultCurrency());
|
|
||||||
|
|
||||||
return resultList;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Currency getDefaultCurrency() {
|
// At OKPay you can exchange internally those currencies
|
||||||
// TODO Only display EUR for the moment
|
public static List<TradeCurrency> getAllOKPayCurrencies() {
|
||||||
return Currency.getInstance("EUR");
|
return new ArrayList<>(Arrays.asList(
|
||||||
// return Currency.getInstance(Locale.getDefault());
|
new FiatCurrency("EUR"),
|
||||||
|
new FiatCurrency("USD"),
|
||||||
|
new FiatCurrency("GBP"),
|
||||||
|
new FiatCurrency("CHF"),
|
||||||
|
new FiatCurrency("RUB"),
|
||||||
|
new FiatCurrency("PLN"),
|
||||||
|
new FiatCurrency("JPY"),
|
||||||
|
new FiatCurrency("CAD"),
|
||||||
|
new FiatCurrency("AUD"),
|
||||||
|
new FiatCurrency("CZK"),
|
||||||
|
new FiatCurrency("NOK"),
|
||||||
|
new FiatCurrency("SEK"),
|
||||||
|
new FiatCurrency("DKK"),
|
||||||
|
new FiatCurrency("HRK"),
|
||||||
|
new FiatCurrency("HUF"),
|
||||||
|
new FiatCurrency("NZD"),
|
||||||
|
new FiatCurrency("RON"),
|
||||||
|
new FiatCurrency("TRY"),
|
||||||
|
new FiatCurrency("ZAR"),
|
||||||
|
new FiatCurrency("HKD"),
|
||||||
|
new FiatCurrency("CNY")
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static List<TradeCurrency> getSortedCryptoCurrencies() {
|
||||||
|
final List<TradeCurrency> result = new ArrayList<>();
|
||||||
|
result.add(new CryptoCurrency("ETH", "Ethereum"));
|
||||||
|
result.add(new CryptoCurrency("LTC", "Litecoin"));
|
||||||
|
result.add(new CryptoCurrency("NMC", "Namecoin"));
|
||||||
|
// Unfortunately we cannot support CryptoNote coins yet as there is no way to proof the transaction. Payment ID helps only locate the tx but the
|
||||||
|
// arbitrator cannot see if the receiving key matches the receivers address. They might add support for exposing the tx key, but that is not
|
||||||
|
// implemented yet. To use the view key (also not available in GUI wallets) would reveal the complete wallet history for incoming payments, which is
|
||||||
|
// not acceptable from pricavy point of view.
|
||||||
|
// result.add(new CryptoCurrency("XMR", "Monero"));
|
||||||
|
// result.add(new CryptoCurrency("BCN", "Bytecoin"));
|
||||||
|
result.add(new CryptoCurrency("DASH", "Dash"));
|
||||||
|
result.add(new CryptoCurrency("ANC", "Anoncoin"));
|
||||||
|
result.add(new CryptoCurrency("NBT", "NuBits"));
|
||||||
|
result.add(new CryptoCurrency("NSR", "NuShares"));
|
||||||
|
result.add(new CryptoCurrency("FAIR", "FairCoin"));
|
||||||
|
result.add(new CryptoCurrency("PPC", "Peercoin"));
|
||||||
|
result.add(new CryptoCurrency("XPM", "Primecoin"));
|
||||||
|
result.add(new CryptoCurrency("DOGE", "Dogecoin"));
|
||||||
|
result.add(new CryptoCurrency("NXT", "Nxt"));
|
||||||
|
result.add(new CryptoCurrency("BTS", "BitShares"));
|
||||||
|
result.add(new CryptoCurrency("XCP", "Counterparty"));
|
||||||
|
result.add(new CryptoCurrency("XRP", "Ripple"));
|
||||||
|
result.add(new CryptoCurrency("STR", "Stellar"));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean isCryptoNoteCoin(String currencyCode) {
|
||||||
|
return currencyCode.equals("XMR") || currencyCode.equals("BCN");
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FiatCurrency getCurrencyByCountryCode(String countryCode) {
|
||||||
|
// java 1.8.8_0_20 reports wrong currency (Lita instead of EUR)
|
||||||
|
if (!countryCode.equals("LT"))
|
||||||
|
return new FiatCurrency(Currency.getInstance(new Locale(LanguageUtil.getDefaultLanguage(), countryCode)).getCurrencyCode());
|
||||||
|
else {
|
||||||
|
return new FiatCurrency("EUR");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static String getNameByCode(String currencyCode) {
|
||||||
|
try {
|
||||||
|
return Currency.getInstance(currencyCode).getDisplayName(Preferences.getDefaultLocale());
|
||||||
|
} catch (Throwable t) {
|
||||||
|
// Seems that it is a crypto currency
|
||||||
|
return getSortedCryptoCurrencies().stream().filter(e -> e.getCode().equals(currencyCode)).findFirst().get().getCodeAndName();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FiatCurrency getDefaultFiatCurrency() {
|
||||||
|
return new FiatCurrency(getCurrencyByCountryCode(CountryUtil.getDefaultCountryCode()).getCurrency());
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -17,38 +17,42 @@
|
|||||||
|
|
||||||
package io.bitsquare.locale;
|
package io.bitsquare.locale;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import io.bitsquare.user.Preferences;
|
||||||
import java.util.Arrays;
|
import org.slf4j.Logger;
|
||||||
import java.util.List;
|
import org.slf4j.LoggerFactory;
|
||||||
import java.util.Locale;
|
|
||||||
import java.util.Set;
|
import java.util.*;
|
||||||
import java.util.stream.Collectors;
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
public class LanguageUtil {
|
public class LanguageUtil {
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(LanguageUtil.class);
|
||||||
|
|
||||||
public static List<String> getAllLanguageLocaleCodes() {
|
public static List<String> getAllLanguageCodes() {
|
||||||
List<Locale> allLocales = Arrays.asList(Locale.getAvailableLocales());
|
List<Locale> allLocales = Arrays.asList(Locale.getAvailableLocales());
|
||||||
final Set<String> allLocaleCodesAsSet =
|
final Set<String> allLocaleCodesAsSet = allLocales.stream()
|
||||||
allLocales.stream().filter(locale -> !"".equals(locale.getLanguage())).map(locale ->
|
.filter(locale -> !"".equals(locale.getLanguage()) && !"".equals(locale.getDisplayLanguage()))
|
||||||
new Locale(locale.getLanguage(), "").getISO3Language()).collect(Collectors.toSet());
|
.map(locale -> new Locale(locale.getLanguage(), "").getLanguage())
|
||||||
|
.collect(Collectors.toSet());
|
||||||
List<String> allLocaleCodes = new ArrayList<>();
|
List<String> allLocaleCodes = new ArrayList<>();
|
||||||
allLocaleCodes.addAll(allLocaleCodesAsSet);
|
allLocaleCodes.addAll(allLocaleCodesAsSet);
|
||||||
allLocaleCodes.sort(String::compareTo);
|
allLocaleCodes.sort((o1, o2) -> getDisplayName(o1).compareTo(getDisplayName(o2)));
|
||||||
return allLocaleCodes;
|
return allLocaleCodes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getDefaultLanguage() {
|
||||||
|
// might be set later in pref or config, so not use Preferences.getDefaultLocale() anywhere in the code
|
||||||
|
return Preferences.getDefaultLocale().getLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
public static String getDefaultLanguageLocaleAsCode() {
|
public static String getDefaultLanguageLocaleAsCode() {
|
||||||
if (Locale.getDefault() != null)
|
return new Locale(LanguageUtil.getDefaultLanguage(), "").getLanguage();
|
||||||
return new Locale(Locale.getDefault().getLanguage(), "").getISO3Language();
|
|
||||||
else
|
|
||||||
return getEnglishLanguageLocaleCode();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getEnglishLanguageLocaleCode() {
|
public static String getEnglishLanguageLocaleCode() {
|
||||||
return new Locale(Locale.ENGLISH.getLanguage(), "").getISO3Language();
|
return new Locale(Locale.ENGLISH.getLanguage(), "").getLanguage();
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getDisplayName(String code) {
|
public static String getDisplayName(String code) {
|
||||||
return new Locale(code).getDisplayName();
|
return new Locale(code.toUpperCase()).getDisplayName(Preferences.getDefaultLocale());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,17 +19,16 @@ package io.bitsquare.locale;
|
|||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import java.io.Serializable;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
public class Region implements Serializable {
|
public class Region implements Serializable {
|
||||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
||||||
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
|
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
|
||||||
|
|
||||||
public final String code;
|
private final String code;
|
||||||
public final String name;
|
private final String name;
|
||||||
|
|
||||||
public Region(String code, String name) {
|
public Region(String code, String name) {
|
||||||
this.code = code;
|
this.code = code;
|
||||||
|
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
|
|
||||||
import io.bitsquare.crypto.PubKeyRing;
|
|
||||||
import io.bitsquare.p2p.listener.GetPeerAddressListener;
|
|
||||||
|
|
||||||
public interface AddressService extends DHTService {
|
|
||||||
void findPeerAddress(PubKeyRing pubKeyRing, GetPeerAddressListener getPeerAddressListener);
|
|
||||||
}
|
|
@ -1,71 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import net.tomp2p.dht.PeerDHT;
|
|
||||||
|
|
||||||
public class BaseP2PService implements P2PService {
|
|
||||||
|
|
||||||
private static Executor userThread;
|
|
||||||
|
|
||||||
public static void setUserThread(Executor userThread) {
|
|
||||||
BaseP2PService.userThread = userThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Executor getUserThread() {
|
|
||||||
return userThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Executor executor;
|
|
||||||
protected PeerDHT peerDHT;
|
|
||||||
private int openRequests = 0;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bootstrapCompleted() {
|
|
||||||
this.executor = BaseP2PService.userThread;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setExecutor(Executor executor) {
|
|
||||||
this.executor = executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
protected void openRequestsUp() {
|
|
||||||
executor.execute(() -> openRequests++);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void openRequestsDown() {
|
|
||||||
executor.execute(() -> openRequests--);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void shutDown() {
|
|
||||||
long ts = System.currentTimeMillis();
|
|
||||||
// wait max. 10 sec. for open calls to complete
|
|
||||||
while (openRequests > 0 && (System.currentTimeMillis() - ts) < 10000) {
|
|
||||||
try {
|
|
||||||
Thread.sleep(100);
|
|
||||||
} catch (InterruptedException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,139 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
import io.bitsquare.BitsquareException;
|
|
||||||
|
|
||||||
import com.google.inject.name.Named;
|
|
||||||
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Random;
|
|
||||||
import java.util.stream.Collectors;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import net.tomp2p.peers.Number160;
|
|
||||||
import net.tomp2p.peers.PeerAddress;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class BootstrapNodes {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(BootstrapNodes.class);
|
|
||||||
public static final String BOOTSTRAP_NODE_KEY = "bootstrapNode";
|
|
||||||
|
|
||||||
private final List<Node> rawBootstrapNodes = Arrays.asList(
|
|
||||||
Node.rawNodeAt("digitalocean1.bitsquare.io", "188.226.179.109"),
|
|
||||||
Node.rawNodeAt("aws1.bitsquare.io", "52.24.144.42"),
|
|
||||||
Node.rawNodeAt("aws2.bitsquare.io", "52.11.125.194")
|
|
||||||
);
|
|
||||||
private Node rawLocalhostNode = Node.rawNodeAt("localhost", "127.0.0.1");
|
|
||||||
|
|
||||||
|
|
||||||
private Node preferredBootstrapNode;
|
|
||||||
private List<Node> bootstrapNodes;
|
|
||||||
private Node localhostNode;
|
|
||||||
private int p2pId;
|
|
||||||
private boolean inited;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public BootstrapNodes(@Named(BOOTSTRAP_NODE_KEY) Node preferredBootstrapNode) {
|
|
||||||
// preferredBootstrapNode need to be fully defined to get accepted (name, IP, p2pId, port)
|
|
||||||
if (preferredBootstrapNode.getName() != null
|
|
||||||
&& preferredBootstrapNode.getIp() != null
|
|
||||||
&& preferredBootstrapNode.getP2pId() != -1
|
|
||||||
&& preferredBootstrapNode.getPort() != -1) {
|
|
||||||
this.preferredBootstrapNode = preferredBootstrapNode;
|
|
||||||
}
|
|
||||||
else if (preferredBootstrapNode.getName() != null
|
|
||||||
|| preferredBootstrapNode.getIp() != null
|
|
||||||
|| preferredBootstrapNode.getP2pId() != -1
|
|
||||||
|| preferredBootstrapNode.getPort() != -1) {
|
|
||||||
log.debug("preferredBootstrapNode not fully defined (name, IP, p2pId, port). preferredBootstrapNode=" + preferredBootstrapNode);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public BootstrapNodes() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public void initWithNetworkId(int p2pId) {
|
|
||||||
if (!inited) {
|
|
||||||
inited = true;
|
|
||||||
this.p2pId = p2pId;
|
|
||||||
if (preferredBootstrapNode != null) {
|
|
||||||
bootstrapNodes = Arrays.asList(preferredBootstrapNode);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
switch (p2pId) {
|
|
||||||
case Node.MAIN_NET_P2P_ID:
|
|
||||||
bootstrapNodes = rawBootstrapNodes.stream()
|
|
||||||
.map(e -> e.withP2pIdAndPort(Node.MAIN_NET_P2P_ID, Node.MAIN_NET_PORT)).collect(Collectors.toList());
|
|
||||||
localhostNode = rawLocalhostNode.withP2pIdAndPort(Node.MAIN_NET_P2P_ID, Node.MAIN_NET_PORT);
|
|
||||||
break;
|
|
||||||
case Node.TEST_NET_P2P_ID:
|
|
||||||
bootstrapNodes = rawBootstrapNodes.stream()
|
|
||||||
.map(e -> e.withP2pIdAndPort(Node.TEST_NET_P2P_ID, Node.TEST_NET_PORT)).collect(Collectors.toList());
|
|
||||||
localhostNode = rawLocalhostNode.withP2pIdAndPort(Node.TEST_NET_P2P_ID, Node.TEST_NET_PORT);
|
|
||||||
break;
|
|
||||||
case Node.REG_TEST_P2P_ID:
|
|
||||||
bootstrapNodes = rawBootstrapNodes.stream()
|
|
||||||
.map(e -> e.withP2pIdAndPort(Node.REG_TEST_P2P_ID, Node.REG_TEST_PORT)).collect(Collectors.toList());
|
|
||||||
localhostNode = rawLocalhostNode.withP2pIdAndPort(Node.REG_TEST_P2P_ID, Node.REG_TEST_PORT);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new BitsquareException("Unsupported P2pId. p2pId=" + p2pId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
throw new BitsquareException("initWithNetworkId called twice");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public Node getRandomDiscoverNode() {
|
|
||||||
return bootstrapNodes.get(new Random().nextInt(bootstrapNodes.size()));
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<Node> getBootstrapNodes() {
|
|
||||||
return bootstrapNodes;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<PeerAddress> getBootstrapPeerAddresses() {
|
|
||||||
return bootstrapNodes.stream().map(e -> {
|
|
||||||
try {
|
|
||||||
return new PeerAddress(Number160.createHash(e.getName()), InetAddress.getByName(e.getIp()), e.getPort(), e.getPort());
|
|
||||||
} catch (UnknownHostException e1) {
|
|
||||||
e1.printStackTrace();
|
|
||||||
log.error(e1.getMessage());
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}).collect(Collectors.toList());
|
|
||||||
}
|
|
||||||
|
|
||||||
public Node getLocalhostNode() {
|
|
||||||
return localhostNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getP2pId() {
|
|
||||||
return p2pId;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
import io.bitsquare.p2p.tomp2p.BootstrappedPeerBuilder;
|
|
||||||
|
|
||||||
import java.security.KeyPair;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
|
||||||
|
|
||||||
import rx.Observable;
|
|
||||||
|
|
||||||
public interface ClientNode {
|
|
||||||
BootstrappedPeerBuilder.ConnectionType getConnectionType();
|
|
||||||
|
|
||||||
String getClientNodeInfo();
|
|
||||||
|
|
||||||
Observable<BootstrappedPeerBuilder.State> bootstrap(KeyPair keyPair);
|
|
||||||
|
|
||||||
ReadOnlyIntegerProperty numPeersProperty();
|
|
||||||
|
|
||||||
void setExecutor(Executor executor);
|
|
||||||
}
|
|
@ -1,45 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
import java.security.PublicKey;
|
|
||||||
|
|
||||||
import net.tomp2p.dht.FutureGet;
|
|
||||||
import net.tomp2p.dht.FuturePut;
|
|
||||||
import net.tomp2p.dht.FutureRemove;
|
|
||||||
import net.tomp2p.peers.Number160;
|
|
||||||
import net.tomp2p.storage.Data;
|
|
||||||
|
|
||||||
public interface DHTService extends P2PService {
|
|
||||||
|
|
||||||
FuturePut putData(Number160 locationKey, Data data);
|
|
||||||
|
|
||||||
FutureGet getData(Number160 locationKey);
|
|
||||||
|
|
||||||
FuturePut putDataToMyProtectedDomain(Number160 locationKey, Data data);
|
|
||||||
|
|
||||||
FutureRemove removeDataFromMyProtectedDomain(Number160 locationKey);
|
|
||||||
|
|
||||||
FutureGet getDataOfProtectedDomain(Number160 locationKey, PublicKey publicKey);
|
|
||||||
|
|
||||||
FuturePut addProtectedDataToMap(Number160 locationKey, Data data);
|
|
||||||
|
|
||||||
FutureRemove removeProtectedDataFromMap(Number160 locationKey, Data data);
|
|
||||||
|
|
||||||
FutureGet getMap(Number160 locationKey);
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
import io.bitsquare.crypto.MessageWithPubKey;
|
|
||||||
|
|
||||||
public interface DecryptedMessageHandler {
|
|
||||||
void handleMessage(MessageWithPubKey message, Peer sender);
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
public interface MailboxMessage extends Message, Serializable {
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
import io.bitsquare.crypto.SealedAndSignedMessage;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public interface MailboxMessagesResultHandler {
|
|
||||||
void handleResult(List<SealedAndSignedMessage> encryptedMessages);
|
|
||||||
}
|
|
@ -1,33 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
import io.bitsquare.common.handlers.FaultHandler;
|
|
||||||
import io.bitsquare.common.handlers.ResultHandler;
|
|
||||||
import io.bitsquare.crypto.PubKeyRing;
|
|
||||||
import io.bitsquare.crypto.SealedAndSignedMessage;
|
|
||||||
|
|
||||||
public interface MailboxService {
|
|
||||||
void addMessage(PubKeyRing pubKeyRing, SealedAndSignedMessage message, ResultHandler resultHandler, FaultHandler faultHandler);
|
|
||||||
|
|
||||||
void getAllMessages(MailboxMessagesResultHandler resultHandler);
|
|
||||||
|
|
||||||
void removeAllMessages(ResultHandler resultHandler, FaultHandler faultHandler);
|
|
||||||
|
|
||||||
void shutDown();
|
|
||||||
}
|
|
@ -1,23 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
public interface Message extends Serializable {
|
|
||||||
}
|
|
@ -1,22 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
public interface MessageHandler {
|
|
||||||
void handleMessage(Message message, Peer sender);
|
|
||||||
}
|
|
@ -1,35 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
|
|
||||||
import io.bitsquare.crypto.PubKeyRing;
|
|
||||||
import io.bitsquare.p2p.listener.SendMessageListener;
|
|
||||||
|
|
||||||
public interface MessageService extends P2PService {
|
|
||||||
|
|
||||||
void sendEncryptedMessage(Peer peer, PubKeyRing pubKeyRing, Message message, boolean useMailboxIfUnreachable, SendMessageListener listener);
|
|
||||||
|
|
||||||
void addMessageHandler(MessageHandler listener);
|
|
||||||
|
|
||||||
void removeMessageHandler(MessageHandler listener);
|
|
||||||
|
|
||||||
void addDecryptedMessageHandler(DecryptedMessageHandler listener);
|
|
||||||
|
|
||||||
void removeDecryptedMessageHandler(DecryptedMessageHandler listener);
|
|
||||||
}
|
|
@ -1,40 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
@SuppressWarnings("serializable")
|
|
||||||
public class NetworkException extends IOException {
|
|
||||||
|
|
||||||
private static final long serialVersionUID = 3635593267998809977L;
|
|
||||||
|
|
||||||
public NetworkException(Throwable cause) {
|
|
||||||
super(cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetworkException(String message) {
|
|
||||||
super(message);
|
|
||||||
}
|
|
||||||
|
|
||||||
public NetworkException(String message, Throwable cause) {
|
|
||||||
super(message, cause);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
@ -1,163 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.net.InetAddress;
|
|
||||||
import java.net.ServerSocket;
|
|
||||||
import java.net.UnknownHostException;
|
|
||||||
|
|
||||||
import net.tomp2p.peers.Number160;
|
|
||||||
import net.tomp2p.peers.PeerAddress;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public final class Node {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(Node.class);
|
|
||||||
|
|
||||||
public static final String NAME_KEY = "node.name";
|
|
||||||
public static final String PORT_KEY = "node.port";
|
|
||||||
public static final String P2P_ID_KEY = "node.p2pId";
|
|
||||||
|
|
||||||
//public static int DEFAULT_PORT = findFreeSystemPort();
|
|
||||||
|
|
||||||
// P2P network ids
|
|
||||||
public static final int MAIN_NET_P2P_ID = 10;
|
|
||||||
public static final int TEST_NET_P2P_ID = 11;
|
|
||||||
public static final int REG_TEST_P2P_ID = 12;
|
|
||||||
|
|
||||||
// ports
|
|
||||||
public static final int MAIN_NET_PORT = 7370;
|
|
||||||
public static final int TEST_NET_PORT = 7371;
|
|
||||||
public static final int REG_TEST_PORT = 7372;
|
|
||||||
|
|
||||||
|
|
||||||
private final String name;
|
|
||||||
private final String ip;
|
|
||||||
private final int port;
|
|
||||||
private final int p2pId;
|
|
||||||
|
|
||||||
private Node(String name, String ip, int p2pId, int port) {
|
|
||||||
this.name = name;
|
|
||||||
this.ip = ip;
|
|
||||||
this.p2pId = p2pId;
|
|
||||||
this.port = port;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Not fully defined node
|
|
||||||
public static Node rawNodeAt(String name, String ip) {
|
|
||||||
return Node.at(name, ip, -1, -1);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Node at(String name, String ip, int p2pId, int port) {
|
|
||||||
return new Node(name, ip, p2pId, port);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Node withP2pIdAndPort(int p2pId, int port) {
|
|
||||||
return Node.at(this.name, this.ip, p2pId, port);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static final int CLIENT_PORT = findFreeSystemPort();
|
|
||||||
|
|
||||||
public static int findFreeSystemPort() {
|
|
||||||
int port = 7369;
|
|
||||||
try {
|
|
||||||
ServerSocket server = new ServerSocket(0);
|
|
||||||
port = server.getLocalPort();
|
|
||||||
log.debug("Random system port used for client: {}", port);
|
|
||||||
server.close();
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PeerAddress toPeerAddressWithPort(int port) {
|
|
||||||
try {
|
|
||||||
return new PeerAddress(Number160.createHash(name),
|
|
||||||
InetAddress.getByName(ip),
|
|
||||||
port,
|
|
||||||
port);
|
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
log.error(e.getMessage());
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public PeerAddress toPeerAddress() {
|
|
||||||
try {
|
|
||||||
return new PeerAddress(Number160.createHash(name),
|
|
||||||
InetAddress.getByName(ip),
|
|
||||||
port,
|
|
||||||
port);
|
|
||||||
} catch (UnknownHostException e) {
|
|
||||||
log.error(e.getMessage());
|
|
||||||
throw new RuntimeException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public String getName() {
|
|
||||||
return name;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getIp() {
|
|
||||||
return ip;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getP2pId() {
|
|
||||||
return p2pId;
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getPort() {
|
|
||||||
return port;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean equals(Object object) {
|
|
||||||
if (this == object)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (object == null || getClass() != object.getClass())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
Node that = (Node) object;
|
|
||||||
return Objects.equal(this.name, that.name) &&
|
|
||||||
Objects.equal(this.ip, that.ip) &&
|
|
||||||
Objects.equal(this.port, that.port);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int hashCode() {
|
|
||||||
return Objects.hashCode(name, ip, port);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString() {
|
|
||||||
return "Name='" + name + '\'' +
|
|
||||||
"; IP='" + ip + '\'' +
|
|
||||||
"; port=" + port +
|
|
||||||
"; P2P network ID=" + p2pId;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,37 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
import io.bitsquare.BitsquareModule;
|
|
||||||
|
|
||||||
import org.springframework.core.env.Environment;
|
|
||||||
|
|
||||||
public abstract class P2PModule extends BitsquareModule {
|
|
||||||
|
|
||||||
protected P2PModule(Environment env) {
|
|
||||||
super(env);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected final void configure() {
|
|
||||||
doConfigure();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void doConfigure() {
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,28 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
public interface P2PService {
|
|
||||||
void bootstrapCompleted();
|
|
||||||
|
|
||||||
void setExecutor(Executor executor);
|
|
||||||
|
|
||||||
void shutDown();
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A peer on the Bitsquare network.
|
|
||||||
*
|
|
||||||
* @author Chris Beams
|
|
||||||
*/
|
|
||||||
public interface Peer {
|
|
||||||
}
|
|
@ -1,26 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p.listener;
|
|
||||||
|
|
||||||
import io.bitsquare.p2p.Peer;
|
|
||||||
|
|
||||||
public interface GetPeerAddressListener {
|
|
||||||
void onResult(Peer peer);
|
|
||||||
|
|
||||||
void onFailed();
|
|
||||||
}
|
|
@ -1,24 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p.listener;
|
|
||||||
|
|
||||||
public interface SendMessageListener {
|
|
||||||
void handleFault();
|
|
||||||
|
|
||||||
void handleResult();
|
|
||||||
}
|
|
@ -1,412 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p.tomp2p;
|
|
||||||
|
|
||||||
import io.bitsquare.p2p.BootstrapNodes;
|
|
||||||
import io.bitsquare.p2p.Node;
|
|
||||||
import io.bitsquare.user.Preferences;
|
|
||||||
|
|
||||||
import com.google.common.util.concurrent.SettableFuture;
|
|
||||||
|
|
||||||
import com.google.inject.name.Named;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.security.KeyPair;
|
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
|
|
||||||
import net.tomp2p.connection.Bindings;
|
|
||||||
import net.tomp2p.connection.ChannelClientConfiguration;
|
|
||||||
import net.tomp2p.connection.ChannelServerConfiguration;
|
|
||||||
import net.tomp2p.dht.PeerBuilderDHT;
|
|
||||||
import net.tomp2p.dht.PeerDHT;
|
|
||||||
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.FutureRelayNAT;
|
|
||||||
import net.tomp2p.nat.PeerBuilderNAT;
|
|
||||||
import net.tomp2p.nat.PeerNAT;
|
|
||||||
import net.tomp2p.p2p.Peer;
|
|
||||||
import net.tomp2p.p2p.PeerBuilder;
|
|
||||||
import net.tomp2p.peers.PeerAddress;
|
|
||||||
import net.tomp2p.peers.PeerMapChangeListener;
|
|
||||||
import net.tomp2p.peers.PeerStatistic;
|
|
||||||
import net.tomp2p.relay.tcp.TCPRelayClientConfig;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import io.netty.util.concurrent.DefaultEventExecutorGroup;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates a DHT peer and bootstraps to the network via a bootstrap node
|
|
||||||
*/
|
|
||||||
public class BootstrappedPeerBuilder {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(BootstrappedPeerBuilder.class);
|
|
||||||
|
|
||||||
static final String NETWORK_INTERFACE_KEY = "interface";
|
|
||||||
static final String NETWORK_INTERFACE_UNSPECIFIED = "<unspecified>";
|
|
||||||
static final String USE_MANUAL_PORT_FORWARDING_KEY = "node.useManualPortForwarding";
|
|
||||||
|
|
||||||
public enum ConnectionType {
|
|
||||||
UNDEFINED, DIRECT, MANUAL_PORT_FORWARDING, AUTO_PORT_FORWARDING, RELAY
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum State {
|
|
||||||
UNDEFINED,
|
|
||||||
PEER_CREATION_FAILED,
|
|
||||||
DISCOVERY_STARTED,
|
|
||||||
DISCOVERY_DIRECT_SUCCEEDED,
|
|
||||||
DISCOVERY_MANUAL_PORT_FORWARDING_SUCCEEDED,
|
|
||||||
DISCOVERY_FAILED,
|
|
||||||
DISCOVERY_AUTO_PORT_FORWARDING_STARTED,
|
|
||||||
DISCOVERY_AUTO_PORT_FORWARDING_SUCCEEDED,
|
|
||||||
DISCOVERY_AUTO_PORT_FORWARDING_FAILED,
|
|
||||||
RELAY_STARTED,
|
|
||||||
RELAY_SUCCEEDED,
|
|
||||||
RELAY_FAILED,
|
|
||||||
BOOT_STRAP_SUCCEEDED,
|
|
||||||
BOOT_STRAP_FAILED;
|
|
||||||
|
|
||||||
private String message;
|
|
||||||
|
|
||||||
State() {
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getMessage() {
|
|
||||||
return message;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setMessage(String message) {
|
|
||||||
this.message = message;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private final int port;
|
|
||||||
private final boolean useManualPortForwarding;
|
|
||||||
private final String networkInterface;
|
|
||||||
private BootstrapNodes bootstrapNodes;
|
|
||||||
private final Preferences preferences;
|
|
||||||
|
|
||||||
private final SettableFuture<PeerDHT> settableFuture = SettableFuture.create();
|
|
||||||
private final ObjectProperty<State> state = new SimpleObjectProperty<>(State.UNDEFINED);
|
|
||||||
private final ObjectProperty<ConnectionType> connectionType = new SimpleObjectProperty<>(ConnectionType.UNDEFINED);
|
|
||||||
|
|
||||||
private KeyPair keyPair;
|
|
||||||
private Peer peer;
|
|
||||||
private PeerDHT peerDHT;
|
|
||||||
private Executor executor;
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Constructor
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public BootstrappedPeerBuilder(@Named(Node.PORT_KEY) int port,
|
|
||||||
@Named(USE_MANUAL_PORT_FORWARDING_KEY) boolean useManualPortForwarding,
|
|
||||||
@Named(NETWORK_INTERFACE_KEY) String networkInterface,
|
|
||||||
BootstrapNodes bootstrapNodes,
|
|
||||||
Preferences preferences) {
|
|
||||||
this.port = port;
|
|
||||||
this.useManualPortForwarding = useManualPortForwarding;
|
|
||||||
this.networkInterface = networkInterface;
|
|
||||||
this.bootstrapNodes = bootstrapNodes;
|
|
||||||
this.preferences = preferences;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Setters
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public void setKeyPair(@NotNull KeyPair keyPair) {
|
|
||||||
this.keyPair = keyPair;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Public methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public void setExecutor(Executor executor) {
|
|
||||||
this.executor = executor;
|
|
||||||
}
|
|
||||||
|
|
||||||
public SettableFuture<PeerDHT> start() {
|
|
||||||
try {
|
|
||||||
DefaultEventExecutorGroup eventExecutorGroup = new DefaultEventExecutorGroup(20);
|
|
||||||
ChannelClientConfiguration clientConf = PeerBuilder.createDefaultChannelClientConfiguration();
|
|
||||||
clientConf.pipelineFilter(new PeerBuilder.EventExecutorGroupFilter(eventExecutorGroup));
|
|
||||||
|
|
||||||
ChannelServerConfiguration serverConf = PeerBuilder.createDefaultChannelServerConfiguration();
|
|
||||||
serverConf.pipelineFilter(new PeerBuilder.EventExecutorGroupFilter(eventExecutorGroup));
|
|
||||||
serverConf.connectionTimeoutTCPMillis(5000);
|
|
||||||
|
|
||||||
Bindings bindings = new Bindings();
|
|
||||||
if (!NETWORK_INTERFACE_UNSPECIFIED.equals(networkInterface))
|
|
||||||
bindings.addInterface(networkInterface);
|
|
||||||
|
|
||||||
if (useManualPortForwarding) {
|
|
||||||
peer = new PeerBuilder(keyPair)
|
|
||||||
.p2pId(bootstrapNodes.getP2pId())
|
|
||||||
.channelClientConfiguration(clientConf)
|
|
||||||
.channelServerConfiguration(serverConf)
|
|
||||||
.ports(port)
|
|
||||||
.bindings(bindings)
|
|
||||||
.tcpPortForwarding(port)
|
|
||||||
.udpPortForwarding(port)
|
|
||||||
.start();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
peer = new PeerBuilder(keyPair)
|
|
||||||
.p2pId(bootstrapNodes.getP2pId())
|
|
||||||
.channelClientConfiguration(clientConf)
|
|
||||||
.channelServerConfiguration(serverConf)
|
|
||||||
.ports(port)
|
|
||||||
.bindings(bindings)
|
|
||||||
.start();
|
|
||||||
}
|
|
||||||
|
|
||||||
peerDHT = new PeerBuilderDHT(peer).start();
|
|
||||||
|
|
||||||
peer.peerBean().peerMap().addPeerMapChangeListener(new PeerMapChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void peerInserted(PeerAddress peerAddress, boolean verified) {
|
|
||||||
if (verified)
|
|
||||||
log.debug("Peer inserted: peerAddress=" + peerAddress + ", verified=" + verified);
|
|
||||||
else
|
|
||||||
log.trace("Peer inserted: peerAddress=" + peerAddress + ", verified=" + verified);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void peerRemoved(PeerAddress peerAddress, PeerStatistic peerStatistics) {
|
|
||||||
log.debug("Peer removed: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void peerUpdated(PeerAddress peerAddress, PeerStatistic peerStatistics) {
|
|
||||||
// log.debug("Peer updated: peerAddress=" + peerAddress + ", peerStatistics=" + peerStatistics);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
if (preferences.getUseUPnP())
|
|
||||||
discoverExternalAddressUsingUPnP();
|
|
||||||
else
|
|
||||||
discoverExternalAddress();
|
|
||||||
} catch (IOException e) {
|
|
||||||
handleError(State.PEER_CREATION_FAILED, "Cannot create a peer with port: " +
|
|
||||||
port + ". Exception: " + e);
|
|
||||||
}
|
|
||||||
|
|
||||||
return settableFuture;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void shutDown() {
|
|
||||||
if (peerDHT != null)
|
|
||||||
peerDHT.shutdown();
|
|
||||||
}
|
|
||||||
|
|
||||||
// We need to discover our external address and test if we are reachable for other nodes
|
|
||||||
// We know our internal address from a discovery of our local network interfaces
|
|
||||||
// We start a discover process with our bootstrap node.
|
|
||||||
// There are 4 cases:
|
|
||||||
// 1. If we are not behind a NAT we get reported back the same address as our internal.
|
|
||||||
// 2. If we are behind a NAT and manual port forwarding is setup we get reported our external address from the
|
|
||||||
// bootstrap node and the bootstrap node could ping us so we know we are reachable.
|
|
||||||
// 3. If we are behind a NAT and the ping probes fails we need to setup port forwarding with UPnP or NAT-PMP.
|
|
||||||
// If that is successfully setup we need to try again a discover so we find out our external address and have
|
|
||||||
// tested successfully our reachability (the additional discover is done internally from startSetupPortforwarding)
|
|
||||||
// 4. If the port forwarding failed we can try as last resort to open a permanent TCP connection to the
|
|
||||||
// bootstrap node and use that peer as relay
|
|
||||||
|
|
||||||
private void discoverExternalAddressUsingUPnP() {
|
|
||||||
Node randomNode = bootstrapNodes.getRandomDiscoverNode();
|
|
||||||
log.info("Random Node for discovering own address visible form outside: " + randomNode);
|
|
||||||
FutureDiscover futureDiscover = peer.discover().peerAddress(randomNode.toPeerAddress()).start();
|
|
||||||
setState(State.DISCOVERY_STARTED);
|
|
||||||
PeerNAT peerNAT = new PeerBuilderNAT(peer).start();
|
|
||||||
FutureNAT futureNAT = peerNAT.startSetupPortforwarding(futureDiscover);
|
|
||||||
FutureRelayNAT futureRelayNAT = peerNAT.startRelay(new TCPRelayClientConfig(), futureDiscover, futureNAT);
|
|
||||||
futureRelayNAT.addListener(new BaseFutureListener<BaseFuture>() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(BaseFuture futureRelayNAT) throws Exception {
|
|
||||||
if (futureDiscover.isSuccess()) {
|
|
||||||
if (useManualPortForwarding) {
|
|
||||||
setState(State.DISCOVERY_MANUAL_PORT_FORWARDING_SUCCEEDED,
|
|
||||||
"NAT traversal successful with manual port forwarding.");
|
|
||||||
setConnectionType(ConnectionType.MANUAL_PORT_FORWARDING);
|
|
||||||
bootstrap();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setState(State.DISCOVERY_DIRECT_SUCCEEDED, "Visible to the network. No NAT traversal needed.");
|
|
||||||
setConnectionType(ConnectionType.DIRECT);
|
|
||||||
bootstrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setState(State.DISCOVERY_AUTO_PORT_FORWARDING_STARTED);
|
|
||||||
if (futureNAT.isSuccess()) {
|
|
||||||
setState(State.DISCOVERY_AUTO_PORT_FORWARDING_SUCCEEDED,
|
|
||||||
"NAT traversal successful with automatic port forwarding.");
|
|
||||||
setConnectionType(ConnectionType.AUTO_PORT_FORWARDING);
|
|
||||||
bootstrap();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (futureRelayNAT.isSuccess()) {
|
|
||||||
// relay mode succeeded
|
|
||||||
setState(State.RELAY_SUCCEEDED, "NAT traversal not successful. Using relay mode.");
|
|
||||||
setConnectionType(ConnectionType.RELAY);
|
|
||||||
bootstrap();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// All attempts failed. Give up...
|
|
||||||
handleError(State.RELAY_FAILED, "NAT traversal using relay mode failed " + futureRelayNAT.failedReason());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(Throwable t) throws Exception {
|
|
||||||
handleError(State.RELAY_FAILED, "Exception at bootstrap: " + t.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void discoverExternalAddress() {
|
|
||||||
Node randomNode = bootstrapNodes.getRandomDiscoverNode();
|
|
||||||
log.info("Random Node for discovering own address visible form outside: " + randomNode);
|
|
||||||
FutureDiscover futureDiscover = peer.discover().peerAddress(randomNode.toPeerAddress()).start();
|
|
||||||
setState(State.DISCOVERY_STARTED);
|
|
||||||
PeerNAT peerNAT = new PeerBuilderNAT(peer).start();
|
|
||||||
FutureRelayNAT futureRelayNAT = peerNAT.startRelay(new TCPRelayClientConfig(), futureDiscover);
|
|
||||||
futureRelayNAT.addListener(new BaseFutureListener<BaseFuture>() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(BaseFuture futureRelayNAT) throws Exception {
|
|
||||||
if (futureDiscover.isSuccess()) {
|
|
||||||
if (useManualPortForwarding) {
|
|
||||||
setState(State.DISCOVERY_MANUAL_PORT_FORWARDING_SUCCEEDED,
|
|
||||||
"NAT traversal successful with manual port forwarding.");
|
|
||||||
setConnectionType(ConnectionType.MANUAL_PORT_FORWARDING);
|
|
||||||
bootstrap();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
setState(State.DISCOVERY_DIRECT_SUCCEEDED, "Visible to the network. No NAT traversal needed.");
|
|
||||||
setConnectionType(ConnectionType.DIRECT);
|
|
||||||
bootstrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (futureRelayNAT.isSuccess()) {
|
|
||||||
// relay mode succeeded
|
|
||||||
setState(State.RELAY_SUCCEEDED, "Using relay mode.");
|
|
||||||
setConnectionType(ConnectionType.RELAY);
|
|
||||||
bootstrap();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// All attempts failed. Give up...
|
|
||||||
handleError(State.RELAY_FAILED, "NAT traversal using relay mode failed " + futureRelayNAT.failedReason());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(Throwable t) throws Exception {
|
|
||||||
handleError(State.RELAY_FAILED, "Exception at bootstrap: " + t.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void bootstrap() {
|
|
||||||
log.trace("start bootstrap");
|
|
||||||
|
|
||||||
// We don't wait until bootstrap is done for speeding up startup process
|
|
||||||
FutureBootstrap futureBootstrap = peer.bootstrap().bootstrapTo(bootstrapNodes.getBootstrapPeerAddresses()).start();
|
|
||||||
futureBootstrap.addListener(new BaseFutureListener<BaseFuture>() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(BaseFuture future) throws Exception {
|
|
||||||
if (futureBootstrap.isSuccess()) {
|
|
||||||
log.trace("bootstrap complete");
|
|
||||||
setState(State.BOOT_STRAP_SUCCEEDED, "Bootstrap was successful.");
|
|
||||||
settableFuture.set(peerDHT);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
handleError(State.BOOT_STRAP_FAILED, "Bootstrap failed. " +
|
|
||||||
futureBootstrap.failedReason());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(Throwable t) throws Exception {
|
|
||||||
handleError(State.BOOT_STRAP_FAILED, "Exception at bootstrap: " + t.getMessage());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public ConnectionType getConnectionType() {
|
|
||||||
return connectionType.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyObjectProperty<ConnectionType> connectionTypeProperty() {
|
|
||||||
return connectionType;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setConnectionType(ConnectionType discoveryState) {
|
|
||||||
this.connectionType.set(discoveryState);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ObjectProperty<State> getState() {
|
|
||||||
return state;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setState(State state) {
|
|
||||||
setState(state, "", true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setState(State state, String message) {
|
|
||||||
setState(state, message, true);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setState(State state, String message, boolean isSuccess) {
|
|
||||||
if (isSuccess)
|
|
||||||
log.info(message);
|
|
||||||
else
|
|
||||||
log.error(message);
|
|
||||||
|
|
||||||
state.setMessage(message);
|
|
||||||
this.state.set(state);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void handleError(State state, String errorMessage) {
|
|
||||||
setState(state, errorMessage, false);
|
|
||||||
peerDHT.shutdown();
|
|
||||||
settableFuture.setException(new Exception(errorMessage));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,172 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p.tomp2p;
|
|
||||||
|
|
||||||
import io.bitsquare.crypto.KeyRing;
|
|
||||||
import io.bitsquare.crypto.PubKeyRing;
|
|
||||||
import io.bitsquare.p2p.AddressService;
|
|
||||||
import io.bitsquare.p2p.NetworkException;
|
|
||||||
import io.bitsquare.p2p.Peer;
|
|
||||||
import io.bitsquare.p2p.listener.GetPeerAddressListener;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.util.Timer;
|
|
||||||
import java.util.TimerTask;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import net.tomp2p.dht.FutureGet;
|
|
||||||
import net.tomp2p.dht.FuturePut;
|
|
||||||
import net.tomp2p.futures.BaseFuture;
|
|
||||||
import net.tomp2p.futures.BaseFutureAdapter;
|
|
||||||
import net.tomp2p.futures.BaseFutureListener;
|
|
||||||
import net.tomp2p.peers.Number160;
|
|
||||||
import net.tomp2p.peers.PeerAddress;
|
|
||||||
import net.tomp2p.storage.Data;
|
|
||||||
import net.tomp2p.utils.Utils;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class TomP2PAddressService extends TomP2PDHTService implements AddressService {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(TomP2PAddressService.class);
|
|
||||||
|
|
||||||
private static final int IP_CHECK_PERIOD = 2 * 60 * 1000; // Cheap call if nothing changes, so set it short to 2 min.
|
|
||||||
private static final int STORE_ADDRESS_PERIOD = 5 * 60 * 1000; // Save every 5 min.
|
|
||||||
private static final int ADDRESS_TTL = STORE_ADDRESS_PERIOD * 2; // TTL 10 min.
|
|
||||||
|
|
||||||
private final Number160 locationKey;
|
|
||||||
private PeerAddress storedPeerAddress;
|
|
||||||
private Timer timerForStoreAddress;
|
|
||||||
private Timer timerForIPCheck;
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Constructor
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public TomP2PAddressService(TomP2PNode tomP2PNode, KeyRing keyRing) {
|
|
||||||
super(tomP2PNode, keyRing);
|
|
||||||
|
|
||||||
locationKey = Utils.makeSHAHash(keyRing.getPubKeyRing().getDhtSignaturePubKey().getEncoded());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bootstrapCompleted() {
|
|
||||||
super.bootstrapCompleted();
|
|
||||||
setupTimerForIPCheck();
|
|
||||||
setupTimerForStoreAddress();
|
|
||||||
storeAddress();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void shutDown() {
|
|
||||||
if (timerForIPCheck != null)
|
|
||||||
timerForIPCheck.cancel();
|
|
||||||
if (timerForStoreAddress != null)
|
|
||||||
timerForStoreAddress.cancel();
|
|
||||||
super.shutDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Find peer address by publicKey
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void findPeerAddress(PubKeyRing pubKeyRing, GetPeerAddressListener listener) {
|
|
||||||
final Number160 locationKey = Utils.makeSHAHash(pubKeyRing.getDhtSignaturePubKey().getEncoded());
|
|
||||||
FutureGet futureGet = getDataOfProtectedDomain(locationKey, pubKeyRing.getDhtSignaturePubKey());
|
|
||||||
log.trace("findPeerAddress called");
|
|
||||||
futureGet.addListener(new BaseFutureAdapter<BaseFuture>() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(BaseFuture baseFuture) throws Exception {
|
|
||||||
if (baseFuture.isSuccess() && futureGet.data() != null) {
|
|
||||||
final Peer peer = (Peer) futureGet.data().object();
|
|
||||||
log.trace("Peer found in DHT. Peer = " + peer);
|
|
||||||
executor.execute(() -> listener.onResult(peer));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.error("getPeerAddress failed. failedReason = " + baseFuture.failedReason());
|
|
||||||
executor.execute(listener::onFailed);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Private
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private void setupTimerForStoreAddress() {
|
|
||||||
timerForStoreAddress = new Timer();
|
|
||||||
timerForStoreAddress.scheduleAtFixedRate(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (storedPeerAddress != null && peerDHT != null && !storedPeerAddress.equals(peerDHT.peerAddress()))
|
|
||||||
storeAddress();
|
|
||||||
}
|
|
||||||
}, STORE_ADDRESS_PERIOD, STORE_ADDRESS_PERIOD);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void setupTimerForIPCheck() {
|
|
||||||
timerForIPCheck = new Timer();
|
|
||||||
timerForIPCheck.scheduleAtFixedRate(new TimerTask() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
if (storedPeerAddress != null && peerDHT != null && !storedPeerAddress.equals(peerDHT.peerAddress()))
|
|
||||||
storeAddress();
|
|
||||||
}
|
|
||||||
}, IP_CHECK_PERIOD, IP_CHECK_PERIOD);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void storeAddress() {
|
|
||||||
try {
|
|
||||||
Data data = new Data(new TomP2PPeer(peerDHT.peerAddress()));
|
|
||||||
// We set a short time-to-live to make getAddress checks fail fast in case if the offerer is offline and to support cheap offerbook state updates
|
|
||||||
data.ttlSeconds(ADDRESS_TTL);
|
|
||||||
log.debug("storePeerAddress " + peerDHT.peerAddress().toString());
|
|
||||||
FuturePut futurePut = putDataToMyProtectedDomain(locationKey, data);
|
|
||||||
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("storedPeerAddress not successful");
|
|
||||||
throw new NetworkException("Storing address was not successful. Reason: " + future.failedReason());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(Throwable t) throws Exception {
|
|
||||||
log.error("Exception at storedPeerAddress " + t.toString());
|
|
||||||
throw new NetworkException("Exception at storeAddress.", t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
log.error("Exception at storePeerAddress " + e.toString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,270 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p.tomp2p;
|
|
||||||
|
|
||||||
import io.bitsquare.crypto.KeyRing;
|
|
||||||
import io.bitsquare.p2p.DHTService;
|
|
||||||
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import net.tomp2p.dht.FutureGet;
|
|
||||||
import net.tomp2p.dht.FuturePut;
|
|
||||||
import net.tomp2p.dht.FutureRemove;
|
|
||||||
import net.tomp2p.dht.StorageLayer;
|
|
||||||
import net.tomp2p.peers.Number160;
|
|
||||||
import net.tomp2p.storage.Data;
|
|
||||||
import net.tomp2p.utils.Utils;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class TomP2PDHTService extends TomP2PService implements DHTService {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(TomP2PDHTService.class);
|
|
||||||
private final KeyPair dhtSignatureKeyPair;
|
|
||||||
private final Number160 pubKeyHashForMyDomain;
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Constructor
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public TomP2PDHTService(TomP2PNode tomP2PNode, KeyRing keyRing) {
|
|
||||||
super(tomP2PNode);
|
|
||||||
|
|
||||||
dhtSignatureKeyPair = keyRing.getDhtSignatureKeyPair();
|
|
||||||
pubKeyHashForMyDomain = Utils.makeSHAHash(dhtSignatureKeyPair.getPublic().getEncoded());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bootstrapCompleted() {
|
|
||||||
super.bootstrapCompleted();
|
|
||||||
|
|
||||||
StorageLayer.ProtectionEnable protectionDomainEnable = StorageLayer.ProtectionEnable.ALL;
|
|
||||||
StorageLayer.ProtectionMode protectionDomainMode = StorageLayer.ProtectionMode.MASTER_PUBLIC_KEY;
|
|
||||||
StorageLayer.ProtectionEnable protectionEntryEnable = StorageLayer.ProtectionEnable.ALL;
|
|
||||||
StorageLayer.ProtectionMode protectionEntryMode = StorageLayer.ProtectionMode.MASTER_PUBLIC_KEY;
|
|
||||||
|
|
||||||
peerDHT.storageLayer().protection(protectionDomainEnable, protectionDomainMode, protectionEntryEnable, protectionEntryMode);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Put/Get: Public access.
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Use case: Used for offerbook invalidation timestamp. Everybody can write that data.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store data to given location key.
|
|
||||||
* Write access: Anyone with locationKey
|
|
||||||
*
|
|
||||||
* @param locationKey
|
|
||||||
* @param data
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public FuturePut putData(Number160 locationKey, Data data) {
|
|
||||||
log.trace("putData");
|
|
||||||
return peerDHT.put(locationKey).data(data).start();
|
|
||||||
}
|
|
||||||
// No protection, everybody can read.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get data for given locationKey
|
|
||||||
* Read access: Anyone with locationKey
|
|
||||||
*
|
|
||||||
* @param locationKey
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public FutureGet getData(Number160 locationKey) {
|
|
||||||
//log.trace("getData");
|
|
||||||
return peerDHT.get(locationKey).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Put/Get: Domain protected, entry protected.
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Use case: Used for storing address. Only domain owner can write and change that data. Data protection gives additional protection (is it needed?)
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Store data to given location key and my domain.
|
|
||||||
* Write access: Anybody who has pubKey if domain is not used before. KeyPair owner of pubKey can overwrite and reserve that domain.
|
|
||||||
* We save early an entry so we have that domain reserved and nobody else can use it.
|
|
||||||
* Additionally we use entry protection, so domain owner is data owner.
|
|
||||||
*
|
|
||||||
* @param locationKey
|
|
||||||
* @param data
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public FuturePut putDataToMyProtectedDomain(Number160 locationKey, Data data) {
|
|
||||||
log.trace("putDataToMyProtectedDomain");
|
|
||||||
data.protectEntry(dhtSignatureKeyPair);
|
|
||||||
return peerDHT.put(locationKey).data(data).protectDomain().domainKey(pubKeyHashForMyDomain).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes data for given location and my domain.
|
|
||||||
* Access: Domain owner only can remove
|
|
||||||
*
|
|
||||||
* @param locationKey
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public FutureRemove removeDataFromMyProtectedDomain(Number160 locationKey) {
|
|
||||||
log.trace("removeDataOfProtectedDomain");
|
|
||||||
if (peerDHT != null)
|
|
||||||
return peerDHT.remove(locationKey).domainKey(pubKeyHashForMyDomain).keyPair(dhtSignatureKeyPair).start();
|
|
||||||
else
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Read data for given location and publicKey of that domain.
|
|
||||||
* Read access: Anyone who has publicKey
|
|
||||||
*
|
|
||||||
* @param locationKey
|
|
||||||
* @param publicKey
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public FutureGet getDataOfProtectedDomain(Number160 locationKey, PublicKey publicKey) {
|
|
||||||
log.trace("getDataOfProtectedDomain");
|
|
||||||
final Number160 pubKeyHashOfDomainOwner = Utils.makeSHAHash(publicKey.getEncoded());
|
|
||||||
return peerDHT.get(locationKey).domainKey(pubKeyHashOfDomainOwner).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Add/remove/get from map: Entry protected, no domain protection.
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Use case: Used for offerbook and arbitrators. Everybody can add entries, but those entries are data protected so only the owner can remove it.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add data to a map. For the entry contentKey of data is used (internally).
|
|
||||||
* Write access: Anyone can add entries. But nobody can overwrite an existing entry as it is protected by data protection.
|
|
||||||
*
|
|
||||||
* @param locationKey
|
|
||||||
* @param data
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public FuturePut addProtectedDataToMap(Number160 locationKey, Data data) {
|
|
||||||
log.trace("addProtectedDataToMap locationKey = " + locationKey);
|
|
||||||
data.protectEntry(dhtSignatureKeyPair);
|
|
||||||
log.trace("addProtectedDataToMap with contentKey " + data.hash().toString());
|
|
||||||
|
|
||||||
return peerDHT.add(locationKey).data(data).keyPair(dhtSignatureKeyPair).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove entry from map for given locationKey. ContentKey of data is used for removing the entry.
|
|
||||||
* Access: Only the owner of the data entry can remove it, as it was written with entry protection.
|
|
||||||
*
|
|
||||||
* @param locationKey
|
|
||||||
* @param data
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public FutureRemove removeProtectedDataFromMap(Number160 locationKey, Data data) {
|
|
||||||
log.trace("removeProtectedDataFromMap locationKey = " + locationKey);
|
|
||||||
Number160 contentKey = data.hash();
|
|
||||||
log.trace("removeProtectedDataFromMap with contentKey " + contentKey.toString());
|
|
||||||
return peerDHT.remove(locationKey).contentKey(contentKey).keyPair(dhtSignatureKeyPair).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get map for given locationKey with all entries.
|
|
||||||
* Access: Everybody can read.
|
|
||||||
*
|
|
||||||
* @param locationKey
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public FutureGet getMap(Number160 locationKey) {
|
|
||||||
log.trace("getMap");
|
|
||||||
return peerDHT.get(locationKey).all().start();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Add/remove/get from map: Domain protection, no data protection.
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
// Use case: Used for mailbox. Everybody can add message entries to ones mailbox, but only mailbox owner (domain owner) can remove entries.
|
|
||||||
// For protecting privacy we use encryption for the messages (not part of DHT infrastructure), so everybody can read the messages but only domain owner
|
|
||||||
// can decrypt it.
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Add data to a map. For the entry contentKey of data is used (internally).
|
|
||||||
* Write access: Anyone can add entries. But nobody expect the domain owner can overwrite/remove an existing entry as it is protected by the domain owner.
|
|
||||||
*
|
|
||||||
* @param locationKey
|
|
||||||
* @param data
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public FuturePut addDataToMapOfProtectedDomain(Number160 locationKey, Data data, PublicKey publicKey) {
|
|
||||||
log.trace("addDataToMapOfProtectedDomain");
|
|
||||||
log.trace("addDataToMapOfProtectedDomain with contentKey " + data.hash().toString());
|
|
||||||
final Number160 pubKeyHashOfDomainOwner = Utils.makeSHAHash(publicKey.getEncoded());
|
|
||||||
return peerDHT.add(locationKey).protectDomain().domainKey(pubKeyHashOfDomainOwner).keyPair(dhtSignatureKeyPair)
|
|
||||||
.data(data).protectDomain().domainKey(pubKeyHashOfDomainOwner).keyPair(dhtSignatureKeyPair).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove entry from map for given locationKey. ContentKey of data is used for removing the entry.
|
|
||||||
* Access: Only the owner of the data entry can remove it, as it was written with entry protection.
|
|
||||||
*
|
|
||||||
* @param locationKey
|
|
||||||
* @param data
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public FutureRemove removeDataFromMapOfMyProtectedDomain(Number160 locationKey, Data data) {
|
|
||||||
log.trace("removeDataFromMapOfMyProtectedDomain");
|
|
||||||
Number160 contentKey = data.hash();
|
|
||||||
log.trace("removeDataFromMapOfMyProtectedDomain with contentKey " + contentKey.toString());
|
|
||||||
return peerDHT.remove(locationKey).contentKey(contentKey).domainKey(pubKeyHashForMyDomain).keyPair(dhtSignatureKeyPair).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Get map for given locationKey with all entries.
|
|
||||||
* Access: Everybody can read.
|
|
||||||
*
|
|
||||||
* @param locationKey
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public FutureGet getDataFromMapOfMyProtectedDomain(Number160 locationKey) {
|
|
||||||
log.trace("getDataFromMapOfMyProtectedDomain");
|
|
||||||
return peerDHT.get(locationKey).all().domainKey(pubKeyHashForMyDomain).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove all data from map for given locationKey.
|
|
||||||
* Access: Only the domain owner.
|
|
||||||
*
|
|
||||||
* @param locationKey
|
|
||||||
* @return
|
|
||||||
*/
|
|
||||||
public FutureRemove removeAllDataFromMapOfMyProtectedDomain(Number160 locationKey) {
|
|
||||||
log.trace("getDataFromMapOfMyProtectedDomain");
|
|
||||||
return peerDHT.remove(locationKey).domainKey(pubKeyHashForMyDomain).keyPair(dhtSignatureKeyPair).all().domainKey(pubKeyHashForMyDomain).keyPair
|
|
||||||
(dhtSignatureKeyPair).start();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
@ -1,188 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p.tomp2p;
|
|
||||||
|
|
||||||
import io.bitsquare.common.handlers.FaultHandler;
|
|
||||||
import io.bitsquare.common.handlers.ResultHandler;
|
|
||||||
import io.bitsquare.crypto.KeyRing;
|
|
||||||
import io.bitsquare.crypto.PubKeyRing;
|
|
||||||
import io.bitsquare.crypto.SealedAndSignedMessage;
|
|
||||||
import io.bitsquare.p2p.MailboxMessagesResultHandler;
|
|
||||||
import io.bitsquare.p2p.MailboxService;
|
|
||||||
import io.bitsquare.trade.offer.OfferBookService;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
|
|
||||||
import java.security.KeyPair;
|
|
||||||
import java.security.PublicKey;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import net.tomp2p.dht.FutureGet;
|
|
||||||
import net.tomp2p.dht.FuturePut;
|
|
||||||
import net.tomp2p.dht.FutureRemove;
|
|
||||||
import net.tomp2p.futures.BaseFuture;
|
|
||||||
import net.tomp2p.futures.BaseFutureAdapter;
|
|
||||||
import net.tomp2p.futures.BaseFutureListener;
|
|
||||||
import net.tomp2p.peers.Number160;
|
|
||||||
import net.tomp2p.peers.Number640;
|
|
||||||
import net.tomp2p.storage.Data;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class TomP2PMailboxService extends TomP2PDHTService implements MailboxService {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(TomP2PMailboxService.class);
|
|
||||||
private static final int TTL = 21 * 24 * 60 * 60; // the message is default 21 days valid, as a max trade period might be about 2 weeks.
|
|
||||||
|
|
||||||
private final List<OfferBookService.Listener> offerRepositoryListeners = new ArrayList<>();
|
|
||||||
private final KeyPair dhtSignatureKeyPair;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public TomP2PMailboxService(TomP2PNode tomP2PNode, KeyRing keyRing) {
|
|
||||||
super(tomP2PNode, keyRing);
|
|
||||||
|
|
||||||
dhtSignatureKeyPair = keyRing.getDhtSignatureKeyPair();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bootstrapCompleted() {
|
|
||||||
super.bootstrapCompleted();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void shutDown() {
|
|
||||||
super.shutDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addMessage(PubKeyRing pubKeyRing, SealedAndSignedMessage message, ResultHandler resultHandler, FaultHandler faultHandler) {
|
|
||||||
try {
|
|
||||||
final Data data = new Data(message);
|
|
||||||
data.ttlSeconds(TTL);
|
|
||||||
Number160 locationKey = getLocationKey(pubKeyRing.getDhtSignaturePubKey());
|
|
||||||
log.trace("Add message to DHT requested. Added data: [locationKey: " + locationKey +
|
|
||||||
", hash: " + data.hash().toString() + "]");
|
|
||||||
|
|
||||||
openRequestsUp();
|
|
||||||
FuturePut futurePut = addDataToMapOfProtectedDomain(locationKey,
|
|
||||||
data, pubKeyRing.getDhtSignaturePubKey());
|
|
||||||
futurePut.addListener(new BaseFutureListener<BaseFuture>() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(BaseFuture future) throws Exception {
|
|
||||||
openRequestsDown();
|
|
||||||
if (future.isSuccess()) {
|
|
||||||
executor.execute(() -> {
|
|
||||||
log.trace("Add message to mailbox was successful. Added data: [locationKey: " + locationKey + ", value: " + data + "]");
|
|
||||||
resultHandler.handleResult();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
// Seems to be a bug in TomP2P that when one peer shuts down the expected nr of peers and the delivered are not matching
|
|
||||||
// As far tested the storage succeeded, so seems to be a wrong message.
|
|
||||||
//Future (compl/canc):true/false, FAILED, Expected 3 result, but got 2
|
|
||||||
|
|
||||||
log.warn("Ignoring isSuccess=false case. failedReason: {}", future.failedReason());
|
|
||||||
resultHandler.handleResult();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(Throwable ex) throws Exception {
|
|
||||||
openRequestsDown();
|
|
||||||
executor.execute(() -> faultHandler.handleFault("Add message to mailbox failed.", ex));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} catch (IOException ex) {
|
|
||||||
openRequestsDown();
|
|
||||||
executor.execute(() -> faultHandler.handleFault("Add message to mailbox failed.", ex));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void getAllMessages(MailboxMessagesResultHandler resultHandler) {
|
|
||||||
log.trace("Get messages from DHT requested for locationKey: " + getLocationKey(dhtSignatureKeyPair.getPublic()));
|
|
||||||
FutureGet futureGet = getDataFromMapOfMyProtectedDomain(getLocationKey(dhtSignatureKeyPair.getPublic()));
|
|
||||||
futureGet.addListener(new BaseFutureAdapter<BaseFuture>() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(BaseFuture future) throws Exception {
|
|
||||||
if (future.isSuccess()) {
|
|
||||||
final Map<Number640, Data> dataMap = futureGet.dataMap();
|
|
||||||
List<SealedAndSignedMessage> messages = new ArrayList<>();
|
|
||||||
if (dataMap != null) {
|
|
||||||
for (Data messageData : dataMap.values()) {
|
|
||||||
try {
|
|
||||||
Object messageDataObject = messageData.object();
|
|
||||||
if (messageDataObject instanceof SealedAndSignedMessage) {
|
|
||||||
messages.add((SealedAndSignedMessage) messageDataObject);
|
|
||||||
}
|
|
||||||
} catch (ClassNotFoundException | IOException e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
executor.execute(() -> resultHandler.handleResult(messages));
|
|
||||||
}
|
|
||||||
|
|
||||||
log.trace("Get messages from DHT was successful. Stored data: [key: " + getLocationKey(dhtSignatureKeyPair.getPublic())
|
|
||||||
+ ", values: " + futureGet.dataMap() + "]");
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
final Map<Number640, Data> dataMap = futureGet.dataMap();
|
|
||||||
if (dataMap == null || dataMap.size() == 0) {
|
|
||||||
log.trace("Get messages from DHT delivered empty dataMap.");
|
|
||||||
executor.execute(() -> resultHandler.handleResult(new ArrayList<>()));
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.error("Get messages from DHT was not successful with reason:" + future.failedReason());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeAllMessages(ResultHandler resultHandler, FaultHandler faultHandler) {
|
|
||||||
log.trace("Remove all messages from DHT requested. locationKey: " + getLocationKey(dhtSignatureKeyPair.getPublic()));
|
|
||||||
FutureRemove futureRemove = removeAllDataFromMapOfMyProtectedDomain(getLocationKey(dhtSignatureKeyPair.getPublic()));
|
|
||||||
futureRemove.addListener(new BaseFutureListener<BaseFuture>() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(BaseFuture future) throws Exception {
|
|
||||||
// We don't test futureRemove.isSuccess() as this API does not fit well to that operation,
|
|
||||||
// it might change in future to something like foundAndRemoved and notFound
|
|
||||||
// See discussion at: https://github.com/tomp2p/TomP2P/issues/57#issuecomment-62069840
|
|
||||||
log.trace("isRemoved? " + futureRemove.isRemoved());
|
|
||||||
executor.execute(resultHandler::handleResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(Throwable t) throws Exception {
|
|
||||||
log.error("Remove all messages from DHT failed. Error: " + t.getMessage());
|
|
||||||
faultHandler.handleFault("Remove all messages from DHT failed. Error: " + t.getMessage(), t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private Number160 getLocationKey(PublicKey p2pSigPubKey) {
|
|
||||||
return Number160.createHash("mailbox" + p2pSigPubKey.hashCode());
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,226 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p.tomp2p;
|
|
||||||
|
|
||||||
import io.bitsquare.crypto.CryptoException;
|
|
||||||
import io.bitsquare.crypto.CryptoService;
|
|
||||||
import io.bitsquare.crypto.MessageWithPubKey;
|
|
||||||
import io.bitsquare.crypto.PubKeyRing;
|
|
||||||
import io.bitsquare.crypto.SealedAndSignedMessage;
|
|
||||||
import io.bitsquare.p2p.DecryptedMessageHandler;
|
|
||||||
import io.bitsquare.p2p.MailboxMessage;
|
|
||||||
import io.bitsquare.p2p.MailboxService;
|
|
||||||
import io.bitsquare.p2p.Message;
|
|
||||||
import io.bitsquare.p2p.MessageHandler;
|
|
||||||
import io.bitsquare.p2p.MessageService;
|
|
||||||
import io.bitsquare.p2p.Peer;
|
|
||||||
import io.bitsquare.p2p.listener.SendMessageListener;
|
|
||||||
import io.bitsquare.util.Utilities;
|
|
||||||
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import net.tomp2p.futures.BaseFuture;
|
|
||||||
import net.tomp2p.futures.BaseFutureListener;
|
|
||||||
import net.tomp2p.futures.FutureDirect;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class TomP2PMessageService extends TomP2PService implements MessageService {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(TomP2PMessageService.class);
|
|
||||||
private static final int MAX_MESSAGE_SIZE = 100 * 1024; // 34 kb is currently the max size used
|
|
||||||
|
|
||||||
private final CopyOnWriteArrayList<MessageHandler> messageHandlers = new CopyOnWriteArrayList<>();
|
|
||||||
private final CopyOnWriteArrayList<DecryptedMessageHandler> decryptedMessageHandlers = new CopyOnWriteArrayList<>();
|
|
||||||
private final MailboxService mailboxService;
|
|
||||||
private final CryptoService<MailboxMessage> cryptoService;
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public TomP2PMessageService(TomP2PNode tomP2PNode, MailboxService mailboxService, CryptoService<MailboxMessage> cryptoService) {
|
|
||||||
super(tomP2PNode);
|
|
||||||
this.mailboxService = mailboxService;
|
|
||||||
this.cryptoService = cryptoService;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void bootstrapCompleted() {
|
|
||||||
super.bootstrapCompleted();
|
|
||||||
setupReplyHandler();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void shutDown() {
|
|
||||||
super.shutDown();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void sendEncryptedMessage(Peer peer, PubKeyRing pubKeyRing, Message message, boolean useMailboxIfUnreachable, SendMessageListener listener) {
|
|
||||||
assert pubKeyRing != null;
|
|
||||||
|
|
||||||
log.debug("sendMessage called");
|
|
||||||
if (peer == null)
|
|
||||||
throw new IllegalArgumentException("Peer must not be null");
|
|
||||||
else if (!(peer instanceof TomP2PPeer))
|
|
||||||
throw new IllegalArgumentException("Peer must be of type TomP2PPeer");
|
|
||||||
|
|
||||||
try {
|
|
||||||
final Message encryptedMessage = cryptoService.encryptAndSignMessage(pubKeyRing, message);
|
|
||||||
|
|
||||||
openRequestsUp();
|
|
||||||
FutureDirect futureDirect = peerDHT.peer().sendDirect(((TomP2PPeer) peer).getPeerAddress()).object(encryptedMessage).start();
|
|
||||||
futureDirect.addListener(new BaseFutureListener<BaseFuture>() {
|
|
||||||
@Override
|
|
||||||
public void operationComplete(BaseFuture future) throws Exception {
|
|
||||||
if (future.isSuccess()) {
|
|
||||||
openRequestsDown();
|
|
||||||
log.debug("sendMessage completed");
|
|
||||||
executor.execute(listener::handleResult);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.info("sendMessage failed. We will try to send the message to the mailbox. Fault reason: " +
|
|
||||||
futureDirect.failedReason());
|
|
||||||
if (useMailboxIfUnreachable) {
|
|
||||||
sendMailboxMessage(pubKeyRing, (SealedAndSignedMessage) encryptedMessage, listener);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
openRequestsDown();
|
|
||||||
log.error("Send message was not successful");
|
|
||||||
executor.execute(listener::handleFault);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void exceptionCaught(Throwable t) throws Exception {
|
|
||||||
log.info("sendMessage failed with exception. We will try to send the message to the mailbox. Exception: "
|
|
||||||
+ t.getMessage());
|
|
||||||
if (useMailboxIfUnreachable) {
|
|
||||||
sendMailboxMessage(pubKeyRing, (SealedAndSignedMessage) encryptedMessage, listener);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
openRequestsDown();
|
|
||||||
log.error("Send message was not successful");
|
|
||||||
executor.execute(listener::handleFault);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
} catch (Throwable t) {
|
|
||||||
openRequestsDown();
|
|
||||||
t.printStackTrace();
|
|
||||||
log.error(t.getMessage());
|
|
||||||
executor.execute(listener::handleFault);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void sendMailboxMessage(PubKeyRing pubKeyRing, SealedAndSignedMessage message, SendMessageListener listener) {
|
|
||||||
log.info("sendMailboxMessage called");
|
|
||||||
mailboxService.addMessage(
|
|
||||||
pubKeyRing,
|
|
||||||
message,
|
|
||||||
() -> {
|
|
||||||
openRequestsDown();
|
|
||||||
log.debug("Message successfully added to peers mailbox.");
|
|
||||||
executor.execute(listener::handleResult);
|
|
||||||
},
|
|
||||||
(errorMessage, throwable) -> {
|
|
||||||
openRequestsDown();
|
|
||||||
log.error("Message failed to add to peers mailbox.");
|
|
||||||
executor.execute(listener::handleFault);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addMessageHandler(MessageHandler listener) {
|
|
||||||
if (!messageHandlers.add(listener))
|
|
||||||
log.error("Add listener did not change list. Probably listener has been already added.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeMessageHandler(MessageHandler listener) {
|
|
||||||
if (!messageHandlers.remove(listener))
|
|
||||||
log.error("Try to remove listener which was never added.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void addDecryptedMessageHandler(DecryptedMessageHandler listener) {
|
|
||||||
if (!decryptedMessageHandlers.add(listener))
|
|
||||||
log.error("Add listener did not change list. Probably listener has been already added.");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void removeDecryptedMessageHandler(DecryptedMessageHandler listener) {
|
|
||||||
if (!decryptedMessageHandlers.remove(listener))
|
|
||||||
log.error("Try to remove listener which was never added.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Private
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
private void setupReplyHandler() {
|
|
||||||
peerDHT.peer().objectDataReply((sender, message) -> {
|
|
||||||
//log.debug("Incoming message with peerAddress " + sender);
|
|
||||||
//log.debug("Incoming message with type " + message);
|
|
||||||
|
|
||||||
int messageSize = 0;
|
|
||||||
if (message != null)
|
|
||||||
messageSize = Utilities.objectToBytArray(message).length;
|
|
||||||
|
|
||||||
log.debug("Incoming message with size " + messageSize);
|
|
||||||
|
|
||||||
if (!sender.equals(peerDHT.peer().peerAddress())) {
|
|
||||||
if (messageSize == 0)
|
|
||||||
log.warn("Received msg is null");
|
|
||||||
else if (messageSize > MAX_MESSAGE_SIZE)
|
|
||||||
log.warn("Received msg size of {} is exceeding the max message size of {}.",
|
|
||||||
Utilities.objectToBytArray(message).length, MAX_MESSAGE_SIZE);
|
|
||||||
else if (message instanceof SealedAndSignedMessage)
|
|
||||||
executor.execute(() -> decryptedMessageHandlers.stream().forEach(e -> {
|
|
||||||
MessageWithPubKey messageWithPubKey = null;
|
|
||||||
try {
|
|
||||||
messageWithPubKey = getDecryptedMessageWithPubKey((SealedAndSignedMessage) message);
|
|
||||||
//log.debug("decrypted message " + messageWithPubKey.getMessage());
|
|
||||||
e.handleMessage(messageWithPubKey, new TomP2PPeer(sender));
|
|
||||||
} catch (CryptoException e1) {
|
|
||||||
e1.printStackTrace();
|
|
||||||
log.warn("decryptAndVerifyMessage msg failed", e1.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
));
|
|
||||||
else if (message instanceof Message)
|
|
||||||
executor.execute(() -> messageHandlers.stream().forEach(e -> e.handleMessage((Message) message, new TomP2PPeer(sender))));
|
|
||||||
else
|
|
||||||
log.warn("We got an object which is not type of Message. Object = " + message);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.error("Received msg from myself. That must never happen.");
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private MessageWithPubKey getDecryptedMessageWithPubKey(SealedAndSignedMessage message) throws CryptoException {
|
|
||||||
return cryptoService.decryptAndVerifyMessage(message);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,88 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p.tomp2p;
|
|
||||||
|
|
||||||
import io.bitsquare.p2p.AddressService;
|
|
||||||
import io.bitsquare.p2p.BootstrapNodes;
|
|
||||||
import io.bitsquare.p2p.ClientNode;
|
|
||||||
import io.bitsquare.p2p.MailboxService;
|
|
||||||
import io.bitsquare.p2p.MessageService;
|
|
||||||
import io.bitsquare.p2p.Node;
|
|
||||||
import io.bitsquare.p2p.P2PModule;
|
|
||||||
|
|
||||||
import com.google.inject.Injector;
|
|
||||||
import com.google.inject.Singleton;
|
|
||||||
import com.google.inject.name.Names;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import org.springframework.core.env.Environment;
|
|
||||||
|
|
||||||
import static io.bitsquare.p2p.tomp2p.BootstrappedPeerBuilder.NETWORK_INTERFACE_UNSPECIFIED;
|
|
||||||
|
|
||||||
public class TomP2PModule extends P2PModule {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(TomP2PModule.class);
|
|
||||||
public static final String BOOTSTRAP_NODE_NAME_KEY = "bootstrap.node.name";
|
|
||||||
public static final String BOOTSTRAP_NODE_IP_KEY = "bootstrap.node.ip";
|
|
||||||
public static final String BOOTSTRAP_NODE_P2P_ID_KEY = "bootstrap.node.p2pId";
|
|
||||||
public static final String BOOTSTRAP_NODE_PORT_KEY = "bootstrap.node.port";
|
|
||||||
public static final String NETWORK_INTERFACE_KEY = BootstrappedPeerBuilder.NETWORK_INTERFACE_KEY;
|
|
||||||
public static final String USE_MANUAL_PORT_FORWARDING_KEY = BootstrappedPeerBuilder.USE_MANUAL_PORT_FORWARDING_KEY;
|
|
||||||
|
|
||||||
public TomP2PModule(Environment env) {
|
|
||||||
super(env);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doConfigure() {
|
|
||||||
// Used both ClientNode and TomP2PNode for injection
|
|
||||||
bind(BootstrapNodes.class).in(Singleton.class);
|
|
||||||
bind(ClientNode.class).to(TomP2PNode.class).in(Singleton.class);
|
|
||||||
bind(TomP2PNode.class).in(Singleton.class);
|
|
||||||
|
|
||||||
bind(BootstrappedPeerBuilder.class).in(Singleton.class);
|
|
||||||
|
|
||||||
bind(AddressService.class).to(TomP2PAddressService.class).in(Singleton.class);
|
|
||||||
bind(MessageService.class).to(TomP2PMessageService.class).in(Singleton.class);
|
|
||||||
bind(MailboxService.class).to(TomP2PMailboxService.class).in(Singleton.class);
|
|
||||||
|
|
||||||
bind(int.class).annotatedWith(Names.named(Node.PORT_KEY)).toInstance(env.getProperty(Node.PORT_KEY, int.class, Node.CLIENT_PORT));
|
|
||||||
bind(boolean.class).annotatedWith(Names.named(USE_MANUAL_PORT_FORWARDING_KEY)).toInstance(
|
|
||||||
env.getProperty(USE_MANUAL_PORT_FORWARDING_KEY, boolean.class, false));
|
|
||||||
|
|
||||||
bind(Node.class).annotatedWith(Names.named(BootstrapNodes.BOOTSTRAP_NODE_KEY)).toInstance(
|
|
||||||
Node.at(env.getProperty(BOOTSTRAP_NODE_NAME_KEY, ""),
|
|
||||||
env.getProperty(BOOTSTRAP_NODE_IP_KEY, ""),
|
|
||||||
Integer.valueOf(env.getProperty(BOOTSTRAP_NODE_P2P_ID_KEY, "-1")),
|
|
||||||
Integer.valueOf(env.getProperty(BOOTSTRAP_NODE_PORT_KEY, "-1"))
|
|
||||||
));
|
|
||||||
bindConstant().annotatedWith(Names.named(NETWORK_INTERFACE_KEY)).to(env.getProperty(NETWORK_INTERFACE_KEY, NETWORK_INTERFACE_UNSPECIFIED));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doClose(Injector injector) {
|
|
||||||
log.trace("doClose " + getClass().getSimpleName());
|
|
||||||
// First shut down AddressService to remove address from DHT
|
|
||||||
injector.getInstance(AddressService.class).shutDown();
|
|
||||||
injector.getInstance(BootstrappedPeerBuilder.class).shutDown();
|
|
||||||
injector.getInstance(MailboxService.class).shutDown();
|
|
||||||
injector.getInstance(MessageService.class).shutDown();
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,196 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p.tomp2p;
|
|
||||||
|
|
||||||
import io.bitsquare.BitsquareException;
|
|
||||||
import io.bitsquare.common.handlers.ResultHandler;
|
|
||||||
import io.bitsquare.p2p.BaseP2PService;
|
|
||||||
import io.bitsquare.p2p.ClientNode;
|
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
|
||||||
import com.google.common.util.concurrent.Futures;
|
|
||||||
import com.google.common.util.concurrent.SettableFuture;
|
|
||||||
|
|
||||||
import java.security.KeyPair;
|
|
||||||
|
|
||||||
import java.util.List;
|
|
||||||
import java.util.concurrent.CopyOnWriteArrayList;
|
|
||||||
import java.util.concurrent.Executor;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import javafx.beans.property.IntegerProperty;
|
|
||||||
import javafx.beans.property.ReadOnlyIntegerProperty;
|
|
||||||
import javafx.beans.property.SimpleIntegerProperty;
|
|
||||||
|
|
||||||
import net.tomp2p.dht.PeerDHT;
|
|
||||||
import net.tomp2p.peers.PeerAddress;
|
|
||||||
import net.tomp2p.peers.PeerMapChangeListener;
|
|
||||||
import net.tomp2p.peers.PeerStatistic;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import rx.Observable;
|
|
||||||
import rx.subjects.BehaviorSubject;
|
|
||||||
import rx.subjects.Subject;
|
|
||||||
|
|
||||||
public class TomP2PNode implements ClientNode {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(TomP2PNode.class);
|
|
||||||
|
|
||||||
private PeerDHT peerDHT;
|
|
||||||
private BootstrappedPeerBuilder bootstrappedPeerBuilder;
|
|
||||||
private final Subject<BootstrappedPeerBuilder.State, BootstrappedPeerBuilder.State> bootstrapStateSubject;
|
|
||||||
private final List<ResultHandler> resultHandlers = new CopyOnWriteArrayList<>();
|
|
||||||
private final IntegerProperty numPeers = new SimpleIntegerProperty(0);
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Constructor
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public TomP2PNode(BootstrappedPeerBuilder bootstrappedPeerBuilder) {
|
|
||||||
this.bootstrappedPeerBuilder = bootstrappedPeerBuilder;
|
|
||||||
bootstrapStateSubject = BehaviorSubject.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
// for unit testing
|
|
||||||
TomP2PNode(KeyPair keyPair, PeerDHT peerDHT) {
|
|
||||||
this.peerDHT = peerDHT;
|
|
||||||
peerDHT.peerBean().keyPair(keyPair);
|
|
||||||
bootstrapStateSubject = BehaviorSubject.create();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Public methods
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
public void setExecutor(Executor executor) {
|
|
||||||
bootstrappedPeerBuilder.setExecutor(executor);
|
|
||||||
}
|
|
||||||
|
|
||||||
public Observable<BootstrappedPeerBuilder.State> bootstrap(KeyPair keyPair) {
|
|
||||||
bootstrappedPeerBuilder.setKeyPair(keyPair);
|
|
||||||
|
|
||||||
bootstrappedPeerBuilder.getState().addListener((ov, oldValue, newValue) -> {
|
|
||||||
log.debug("BootstrapState changed " + newValue);
|
|
||||||
bootstrapStateSubject.onNext(newValue);
|
|
||||||
});
|
|
||||||
|
|
||||||
SettableFuture<PeerDHT> bootstrapFuture = bootstrappedPeerBuilder.start();
|
|
||||||
Futures.addCallback(bootstrapFuture, new FutureCallback<PeerDHT>() {
|
|
||||||
@Override
|
|
||||||
public void onSuccess(@Nullable PeerDHT peerDHT) {
|
|
||||||
if (peerDHT != null) {
|
|
||||||
TomP2PNode.this.peerDHT = peerDHT;
|
|
||||||
|
|
||||||
BaseP2PService.getUserThread().execute(() -> numPeers.set(peerDHT.peerBean().peerMap().all().size()));
|
|
||||||
log.debug("Number of peers = " + peerDHT.peerBean().peerMap().all().size());
|
|
||||||
|
|
||||||
peerDHT.peerBean().peerMap().addPeerMapChangeListener(new PeerMapChangeListener() {
|
|
||||||
@Override
|
|
||||||
public void peerInserted(PeerAddress peerAddress, boolean b) {
|
|
||||||
BaseP2PService.getUserThread().execute(() -> numPeers.set(peerDHT.peerBean().peerMap().all().size()));
|
|
||||||
log.debug("peerInserted " + peerAddress);
|
|
||||||
log.debug("Number of peers = " + peerDHT.peerBean().peerMap().all().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void peerRemoved(PeerAddress peerAddress, PeerStatistic peerStatistic) {
|
|
||||||
BaseP2PService.getUserThread().execute(() -> numPeers.set(peerDHT.peerBean().peerMap().all().size()));
|
|
||||||
log.debug("peerRemoved " + peerAddress);
|
|
||||||
log.debug("Number of peers = " + peerDHT.peerBean().peerMap().all().size());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void peerUpdated(PeerAddress peerAddress, PeerStatistic peerStatistic) {
|
|
||||||
BaseP2PService.getUserThread().execute(() -> numPeers.set(peerDHT.peerBean().peerMap().all().size()));
|
|
||||||
// log.debug("peerUpdated " + peerAddress);
|
|
||||||
// log.debug("Number of peers = " + peerDHT.peerBean().peerMap().all().size());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
/* peerDHT.peerBean().addPeerStatusListener(new PeerStatusListener() {
|
|
||||||
@Override
|
|
||||||
public boolean peerFailed(PeerAddress peerAddress, PeerException e) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean peerFound(PeerAddress peerAddress, PeerAddress peerAddress1, PeerConnection peerConnection, RTT rtt) {
|
|
||||||
BaseP2PService.getUserThread().execute(() -> numPeers.set(peerDHT.peerBean().peerMap().size()));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
});*/
|
|
||||||
|
|
||||||
resultHandlers.stream().forEach(ResultHandler::handleResult);
|
|
||||||
bootstrapStateSubject.onCompleted();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
log.error("Error at bootstrap: peerDHT = null");
|
|
||||||
bootstrapStateSubject.onError(new BitsquareException("Error at bootstrap: peerDHT = null"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFailure(@NotNull Throwable t) {
|
|
||||||
log.error("Exception at bootstrap " + t.getMessage());
|
|
||||||
bootstrapStateSubject.onError(t);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return bootstrapStateSubject.asObservable();
|
|
||||||
}
|
|
||||||
|
|
||||||
public PeerDHT getPeerDHT() {
|
|
||||||
return peerDHT;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public BootstrappedPeerBuilder.ConnectionType getConnectionType() {
|
|
||||||
return bootstrappedPeerBuilder.getConnectionType();
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getClientNodeInfo() {
|
|
||||||
PeerAddress peerAddress = peerDHT.peerBean().serverPeerAddress();
|
|
||||||
return "IP='" + peerAddress.inetAddress().getHostAddress() + '\'' +
|
|
||||||
"; P2P network ID='" + peerDHT.peer().p2pId() + '\'' +
|
|
||||||
"; port=" + peerAddress.peerSocketAddress().tcpPort();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void addResultHandler(ResultHandler resultHandler) {
|
|
||||||
resultHandlers.add(resultHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void removeResultHandler(ResultHandler resultHandler) {
|
|
||||||
resultHandlers.remove(resultHandler);
|
|
||||||
}
|
|
||||||
|
|
||||||
public int getNumPeers() {
|
|
||||||
return numPeers.get();
|
|
||||||
}
|
|
||||||
|
|
||||||
public ReadOnlyIntegerProperty numPeersProperty() {
|
|
||||||
return numPeers;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,56 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p.tomp2p;
|
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
|
||||||
import io.bitsquare.p2p.Peer;
|
|
||||||
|
|
||||||
import com.google.common.base.Objects;
|
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
|
||||||
|
|
||||||
import net.tomp2p.peers.PeerAddress;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* A {@link Peer} implementation that encapsulates a TomP2P {@link PeerAddress}.
|
|
||||||
*
|
|
||||||
* @author Chris Beams
|
|
||||||
*/
|
|
||||||
@Immutable
|
|
||||||
public class TomP2PPeer implements Peer, Serializable {
|
|
||||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
|
||||||
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
|
|
||||||
|
|
||||||
private final PeerAddress peerAddress;
|
|
||||||
|
|
||||||
public TomP2PPeer(PeerAddress peerAddress) {
|
|
||||||
this.peerAddress = peerAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public PeerAddress getPeerAddress() {
|
|
||||||
return peerAddress;
|
|
||||||
}
|
|
||||||
|
|
||||||
public String toString() {
|
|
||||||
return Objects.toStringHelper(this)
|
|
||||||
.add("peerAddress", peerAddress)
|
|
||||||
.toString();
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,50 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.p2p.tomp2p;
|
|
||||||
|
|
||||||
import io.bitsquare.p2p.BaseP2PService;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* That service 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 service.
|
|
||||||
* That way we limit the dependency of the TomP2P library only to that class (and it's sub components).
|
|
||||||
* <p/>
|
|
||||||
*/
|
|
||||||
public class TomP2PService extends BaseP2PService {
|
|
||||||
private static final Logger log = LoggerFactory.getLogger(TomP2PService.class);
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Constructor
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
public TomP2PService(TomP2PNode tomP2PNode) {
|
|
||||||
tomP2PNode.addResultHandler(() -> {
|
|
||||||
peerDHT = tomP2PNode.getPeerDHT();
|
|
||||||
bootstrapCompleted();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
@ -18,13 +18,13 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright 2013 Google Inc.
|
* Copyright 2013 Google Inc.
|
||||||
* Copyright 2014 Andreas Schildbach
|
* Copyright 2014 Andreas Schildbach
|
||||||
* <p/>
|
* <p>
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
* You may obtain a copy of the License at
|
* You may obtain a copy of the License at
|
||||||
* <p/>
|
* <p>
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
* <p/>
|
* <p>
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
@ -35,30 +35,22 @@
|
|||||||
package io.bitsquare.storage;
|
package io.bitsquare.storage;
|
||||||
|
|
||||||
|
|
||||||
import org.bitcoinj.core.Utils;
|
|
||||||
import org.bitcoinj.utils.Threading;
|
|
||||||
|
|
||||||
import com.google.common.io.Files;
|
import com.google.common.io.Files;
|
||||||
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
import com.google.common.util.concurrent.ThreadFactoryBuilder;
|
||||||
|
import io.bitsquare.common.UserThread;
|
||||||
|
import org.bitcoinj.core.Utils;
|
||||||
|
import org.bitcoinj.utils.Threading;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
import java.io.FileInputStream;
|
|
||||||
import java.io.FileOutputStream;
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.ObjectInputStream;
|
|
||||||
import java.io.ObjectOutputStream;
|
|
||||||
|
|
||||||
import java.nio.file.Paths;
|
import java.nio.file.Paths;
|
||||||
|
|
||||||
import java.util.concurrent.Callable;
|
import java.util.concurrent.Callable;
|
||||||
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
import java.util.concurrent.ScheduledThreadPoolExecutor;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
import java.util.concurrent.atomic.AtomicBoolean;
|
||||||
import java.util.concurrent.locks.ReentrantLock;
|
import java.util.concurrent.locks.ReentrantLock;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import static com.google.common.base.Preconditions.checkNotNull;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -97,17 +89,17 @@ public class FileManager<T> {
|
|||||||
.setPriority(Thread.MIN_PRIORITY); // Avoid competing with the GUI thread.
|
.setPriority(Thread.MIN_PRIORITY); // Avoid competing with the GUI thread.
|
||||||
|
|
||||||
// An executor that starts up threads when needed and shuts them down later.
|
// An executor that starts up threads when needed and shuts them down later.
|
||||||
this.executor = new ScheduledThreadPoolExecutor(1, builder.build());
|
executor = new ScheduledThreadPoolExecutor(1, builder.build());
|
||||||
this.executor.setKeepAliveTime(5, TimeUnit.SECONDS);
|
executor.setKeepAliveTime(5, TimeUnit.SECONDS);
|
||||||
this.executor.allowCoreThreadTimeOut(true);
|
executor.allowCoreThreadTimeOut(true);
|
||||||
this.executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
executor.setExecuteExistingDelayedTasksAfterShutdownPolicy(false);
|
||||||
|
|
||||||
// File must only be accessed from the auto-save executor from now on, to avoid simultaneous access.
|
// File must only be accessed from the auto-save executor from now on, to avoid simultaneous access.
|
||||||
this.savePending = new AtomicBoolean();
|
savePending = new AtomicBoolean();
|
||||||
this.delay = delay;
|
this.delay = delay;
|
||||||
this.delayTimeUnit = checkNotNull(delayTimeUnit);
|
this.delayTimeUnit = checkNotNull(delayTimeUnit);
|
||||||
|
|
||||||
this.saver = () -> {
|
saver = () -> {
|
||||||
// Runs in an auto save thread.
|
// Runs in an auto save thread.
|
||||||
if (!savePending.getAndSet(false)) {
|
if (!savePending.getAndSet(false)) {
|
||||||
// Some other scheduled request already beat us to it.
|
// Some other scheduled request already beat us to it.
|
||||||
@ -121,7 +113,7 @@ public class FileManager<T> {
|
|||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
try {
|
try {
|
||||||
FileManager.this.shutdown();
|
FileManager.this.shutDown();
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
throw new RuntimeException(e);
|
throw new RuntimeException(e);
|
||||||
}
|
}
|
||||||
@ -137,7 +129,7 @@ public class FileManager<T> {
|
|||||||
/**
|
/**
|
||||||
* Actually write the wallet file to disk, using an atomic rename when possible. Runs on the current thread.
|
* Actually write the wallet file to disk, using an atomic rename when possible. Runs on the current thread.
|
||||||
*/
|
*/
|
||||||
public void saveNow(T serializable) throws IOException {
|
public void saveNow(T serializable) {
|
||||||
saveNowInternal(serializable);
|
saveNowInternal(serializable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,12 +144,15 @@ public class FileManager<T> {
|
|||||||
executor.schedule(saver, delay, delayTimeUnit);
|
executor.schedule(saver, delay, delayTimeUnit);
|
||||||
}
|
}
|
||||||
|
|
||||||
public T read(File file) throws IOException, ClassNotFoundException {
|
public T read(File file) {
|
||||||
log.debug("read" + file);
|
log.debug("read" + file);
|
||||||
lock.lock();
|
lock.lock();
|
||||||
try (final FileInputStream fileInputStream = new FileInputStream(file);
|
try (final FileInputStream fileInputStream = new FileInputStream(file);
|
||||||
final ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
|
final ObjectInputStream objectInputStream = new ObjectInputStream(fileInputStream)) {
|
||||||
return (T) objectInputStream.readObject();
|
return (T) objectInputStream.readObject();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
log.error("Exception at read: " + t.getMessage());
|
||||||
|
return null;
|
||||||
} finally {
|
} finally {
|
||||||
lock.unlock();
|
lock.unlock();
|
||||||
}
|
}
|
||||||
@ -190,7 +185,7 @@ public class FileManager<T> {
|
|||||||
/**
|
/**
|
||||||
* Shut down auto-saving.
|
* Shut down auto-saving.
|
||||||
*/
|
*/
|
||||||
public void shutdown() {
|
public void shutDown() {
|
||||||
/* if (serializable != null)
|
/* if (serializable != null)
|
||||||
log.debug("shutDown " + serializable.getClass().getSimpleName());
|
log.debug("shutDown " + serializable.getClass().getSimpleName());
|
||||||
else
|
else
|
||||||
@ -198,9 +193,10 @@ public class FileManager<T> {
|
|||||||
|
|
||||||
executor.shutdown();
|
executor.shutdown();
|
||||||
try {
|
try {
|
||||||
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); // forever
|
//executor.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS); // forever
|
||||||
} catch (InterruptedException x) {
|
executor.awaitTermination(5, TimeUnit.SECONDS);
|
||||||
throw new RuntimeException(x);
|
} catch (InterruptedException e) {
|
||||||
|
Thread.currentThread().interrupt();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -241,7 +237,7 @@ public class FileManager<T> {
|
|||||||
private void saveNowInternal(T serializable) {
|
private void saveNowInternal(T serializable) {
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
saveToFile(serializable, dir, storageFile);
|
saveToFile(serializable, dir, storageFile);
|
||||||
Threading.USER_THREAD.execute(() -> log.info("Save {} completed in {}msec", storageFile, System.currentTimeMillis() - now));
|
UserThread.execute(() -> log.info("Save {} completed in {}msec", storageFile, System.currentTimeMillis() - now));
|
||||||
}
|
}
|
||||||
|
|
||||||
private void saveToFile(T serializable, File dir, File storageFile) {
|
private void saveToFile(T serializable, File dir, File storageFile) {
|
||||||
@ -264,7 +260,6 @@ public class FileManager<T> {
|
|||||||
|
|
||||||
// TODO ConcurrentModificationException happens sometimes at that line
|
// TODO ConcurrentModificationException happens sometimes at that line
|
||||||
objectOutputStream.writeObject(serializable);
|
objectOutputStream.writeObject(serializable);
|
||||||
|
|
||||||
// Attempt to force the bits to hit the disk. In reality the OS or hard disk itself may still decide
|
// Attempt to force the bits to hit the disk. In reality the OS or hard disk itself may still decide
|
||||||
// to not write through to physical media for at least a few seconds, but this is the best we can do.
|
// to not write through to physical media for at least a few seconds, but this is the best we can do.
|
||||||
fileOutputStream.flush();
|
fileOutputStream.flush();
|
||||||
@ -313,8 +308,7 @@ public class FileManager<T> {
|
|||||||
if (!tempFile.renameTo(canonical)) {
|
if (!tempFile.renameTo(canonical)) {
|
||||||
throw new IOException("Failed to rename " + tempFile + " to " + canonical);
|
throw new IOException("Failed to rename " + tempFile + " to " + canonical);
|
||||||
}
|
}
|
||||||
}
|
} else if (!tempFile.renameTo(file)) {
|
||||||
else if (!tempFile.renameTo(file)) {
|
|
||||||
throw new IOException("Failed to rename " + tempFile + " to " + file);
|
throw new IOException("Failed to rename " + tempFile + " to " + file);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
|
@ -18,20 +18,18 @@
|
|||||||
package io.bitsquare.storage;
|
package io.bitsquare.storage;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
import com.google.common.base.Throwables;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* That class handles the storage of a particular object to disk using Java serialisation.
|
* That class handles the storage of a particular object to disk using Java serialisation.
|
||||||
@ -39,15 +37,15 @@ import org.slf4j.LoggerFactory;
|
|||||||
* Java serialisation is tolerant with added fields, but removing or changing existing fields will break the backwards compatibility.
|
* Java serialisation is tolerant with added fields, but removing or changing existing fields will break the backwards compatibility.
|
||||||
* Alternative frameworks for serialisation like Kyro or mapDB have shown problems with version migration, so we stuck with plain Java
|
* Alternative frameworks for serialisation like Kyro or mapDB have shown problems with version migration, so we stuck with plain Java
|
||||||
* serialisation.
|
* serialisation.
|
||||||
* <p/>
|
* <p>
|
||||||
* For every data object we write a separate file to minimize the risk of corrupted files in case of inconsistency from newer versions.
|
* For every data object we write a separate file to minimize the risk of corrupted files in case of inconsistency from newer versions.
|
||||||
* In case of a corrupted file we backup the old file to a separate directory, so if it holds critical data it might be helpful for recovery.
|
* In case of a corrupted file we backup the old file to a separate directory, so if it holds critical data it might be helpful for recovery.
|
||||||
* <p/>
|
* <p>
|
||||||
* We also backup at first read the file, so we have a valid file form the latest version in case a write operation corrupted the file.
|
* We also backup at first read the file, so we have a valid file form the latest version in case a write operation corrupted the file.
|
||||||
* <p/>
|
* <p>
|
||||||
* The read operation is triggered just at object creation (startup) and is at the moment not executed on a background thread to avoid asynchronous behaviour.
|
* The read operation is triggered just at object creation (startup) and is at the moment not executed on a background thread to avoid asynchronous behaviour.
|
||||||
* As the data are small and it is just one read access the performance penalty is small and might be even worse to create and setup a thread for it.
|
* As the data are small and it is just one read access the performance penalty is small and might be even worse to create and setup a thread for it.
|
||||||
* <p/>
|
* <p>
|
||||||
* The write operation used a background thread and supports a delayed write to avoid too many repeated write operations.
|
* The write operation used a background thread and supports a delayed write to avoid too many repeated write operations.
|
||||||
*/
|
*/
|
||||||
public class Storage<T extends Serializable> {
|
public class Storage<T extends Serializable> {
|
||||||
@ -98,8 +96,7 @@ public class Storage<T extends Serializable> {
|
|||||||
// Save delayed and on a background thread
|
// Save delayed and on a background thread
|
||||||
public void queueUpForSave() {
|
public void queueUpForSave() {
|
||||||
log.debug("save " + fileName);
|
log.debug("save " + fileName);
|
||||||
if (storageFile == null)
|
checkNotNull(storageFile, "storageFile = null. Call setupFileStorage before using read/write.");
|
||||||
throw new RuntimeException("storageFile = null. Call setupFileStorage before using read/write.");
|
|
||||||
|
|
||||||
fileManager.saveLater(serializable);
|
fileManager.saveLater(serializable);
|
||||||
}
|
}
|
||||||
@ -129,7 +126,7 @@ public class Storage<T extends Serializable> {
|
|||||||
log.info("Backup {} completed in {}msec", serializable.getClass().getSimpleName(), System.currentTimeMillis() - now);
|
log.info("Backup {} completed in {}msec", serializable.getClass().getSimpleName(), System.currentTimeMillis() - now);
|
||||||
|
|
||||||
return persistedObject;
|
return persistedObject;
|
||||||
} catch (ClassCastException | ClassNotFoundException | IOException e) {
|
} catch (ClassCastException | IOException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.error("Version of persisted class has changed. We cannot read the persisted data anymore. We make a backup and remove the inconsistent " +
|
log.error("Version of persisted class has changed. We cannot read the persisted data anymore. We make a backup and remove the inconsistent " +
|
||||||
"file.");
|
"file.");
|
||||||
|
@ -18,21 +18,22 @@
|
|||||||
package io.bitsquare.trade;
|
package io.bitsquare.trade;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.p2p.Peer;
|
import io.bitsquare.btc.FeePolicy;
|
||||||
|
import io.bitsquare.p2p.Address;
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.trade.offer.Offer;
|
import io.bitsquare.trade.offer.Offer;
|
||||||
import io.bitsquare.trade.protocol.trade.BuyerAsOffererProtocol;
|
import io.bitsquare.trade.protocol.trade.BuyerAsOffererProtocol;
|
||||||
import io.bitsquare.trade.protocol.trade.OffererProtocol;
|
import io.bitsquare.trade.protocol.trade.OffererProtocol;
|
||||||
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
|
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class BuyerAsOffererTrade extends BuyerTrade implements OffererTrade, Serializable {
|
public class BuyerAsOffererTrade extends BuyerTrade implements OffererTrade, Serializable {
|
||||||
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
||||||
@ -50,10 +51,13 @@ public class BuyerAsOffererTrade extends BuyerTrade implements OffererTrade, Ser
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||||
in.defaultReadObject();
|
try {
|
||||||
|
in.defaultReadObject();
|
||||||
initStateProperties();
|
initStateProperties();
|
||||||
initAmountProperty();
|
initAmountProperty();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
log.trace("Cannot be deserialized." + t.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -67,13 +71,15 @@ public class BuyerAsOffererTrade extends BuyerTrade implements OffererTrade, Ser
|
|||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleTakeOfferRequest(TradeMessage message, Peer taker) {
|
public void handleTakeOfferRequest(TradeMessage message, Address taker) {
|
||||||
((OffererProtocol) tradeProtocol).handleTakeOfferRequest(message, taker);
|
((OffererProtocol) tradeProtocol).handleTakeOfferRequest(message, taker);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Coin getPayoutAmount() {
|
public Coin getPayoutAmount() {
|
||||||
return getSecurityDeposit().add(getTradeAmount());
|
checkNotNull(getTradeAmount(), "Invalid state: getTradeAmount() = null");
|
||||||
|
|
||||||
|
return FeePolicy.SECURITY_DEPOSIT.add(getTradeAmount());
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,20 +18,22 @@
|
|||||||
package io.bitsquare.trade;
|
package io.bitsquare.trade;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.p2p.Peer;
|
import io.bitsquare.btc.FeePolicy;
|
||||||
|
import io.bitsquare.p2p.Address;
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.trade.offer.Offer;
|
import io.bitsquare.trade.offer.Offer;
|
||||||
import io.bitsquare.trade.protocol.trade.BuyerAsTakerProtocol;
|
import io.bitsquare.trade.protocol.trade.BuyerAsTakerProtocol;
|
||||||
import io.bitsquare.trade.protocol.trade.TakerProtocol;
|
import io.bitsquare.trade.protocol.trade.TakerProtocol;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import org.slf4j.LoggerFactory;
|
import static com.google.common.base.Preconditions.checkNotNull;
|
||||||
|
|
||||||
public class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade, Serializable {
|
public class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade, Serializable {
|
||||||
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
||||||
@ -44,15 +46,18 @@ public class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade, Seriali
|
|||||||
// Constructor, initialization
|
// Constructor, initialization
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public BuyerAsTakerTrade(Offer offer, Coin tradeAmount, Peer tradingPeer, Storage<? extends TradableList> storage) {
|
public BuyerAsTakerTrade(Offer offer, Coin tradeAmount, Address tradingPeerAddress, Storage<? extends TradableList> storage) {
|
||||||
super(offer, tradeAmount, tradingPeer, storage);
|
super(offer, tradeAmount, tradingPeerAddress, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||||
in.defaultReadObject();
|
try {
|
||||||
|
in.defaultReadObject();
|
||||||
initStateProperties();
|
initStateProperties();
|
||||||
initAmountProperty();
|
initAmountProperty();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
log.trace("Cannot be deserialized." + t.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -67,12 +72,14 @@ public class BuyerAsTakerTrade extends BuyerTrade implements TakerTrade, Seriali
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void takeAvailableOffer() {
|
public void takeAvailableOffer() {
|
||||||
assert tradeProtocol instanceof TakerProtocol;
|
checkArgument(tradeProtocol instanceof TakerProtocol, "tradeProtocol NOT instanceof TakerProtocol");
|
||||||
((TakerProtocol) tradeProtocol).takeAvailableOffer();
|
((TakerProtocol) tradeProtocol).takeAvailableOffer();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Coin getPayoutAmount() {
|
public Coin getPayoutAmount() {
|
||||||
return getSecurityDeposit().add(getTradeAmount());
|
checkNotNull(getTradeAmount(), "Invalid state: getTradeAmount() = null");
|
||||||
|
|
||||||
|
return FeePolicy.SECURITY_DEPOSIT.add(getTradeAmount());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,19 +18,17 @@
|
|||||||
package io.bitsquare.trade;
|
package io.bitsquare.trade;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.p2p.Peer;
|
import io.bitsquare.p2p.Address;
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.trade.offer.Offer;
|
import io.bitsquare.trade.offer.Offer;
|
||||||
import io.bitsquare.trade.protocol.trade.BuyerProtocol;
|
import io.bitsquare.trade.protocol.trade.BuyerProtocol;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import java.util.Date;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public abstract class BuyerTrade extends Trade implements Serializable {
|
public abstract class BuyerTrade extends Trade implements Serializable {
|
||||||
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
||||||
@ -38,8 +36,8 @@ public abstract class BuyerTrade extends Trade implements Serializable {
|
|||||||
|
|
||||||
transient private static final Logger log = LoggerFactory.getLogger(BuyerAsOffererTrade.class);
|
transient private static final Logger log = LoggerFactory.getLogger(BuyerAsOffererTrade.class);
|
||||||
|
|
||||||
public BuyerTrade(Offer offer, Coin tradeAmount, Peer tradingPeer, Storage<? extends TradableList> storage) {
|
public BuyerTrade(Offer offer, Coin tradeAmount, Address tradingPeerAddress, Storage<? extends TradableList> storage) {
|
||||||
super(offer, tradeAmount, tradingPeer, storage);
|
super(offer, tradeAmount, tradingPeerAddress, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public BuyerTrade(Offer offer, Storage<? extends TradableList> storage) {
|
public BuyerTrade(Offer offer, Storage<? extends TradableList> storage) {
|
||||||
@ -48,92 +46,26 @@ public abstract class BuyerTrade extends Trade implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initStates() {
|
protected void initStates() {
|
||||||
if (tradeState == null)
|
if (state == null)
|
||||||
tradeState = TradeState.BuyerState.PREPARATION;
|
state = State.PREPARATION;
|
||||||
initStateProperties();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onFiatPaymentStarted() {
|
public void onFiatPaymentStarted() {
|
||||||
assert tradeProtocol instanceof BuyerProtocol;
|
checkArgument(tradeProtocol instanceof BuyerProtocol, "tradeProtocol NOT instanceof BuyerProtocol");
|
||||||
((BuyerProtocol) tradeProtocol).onFiatPaymentStarted();
|
((BuyerProtocol) tradeProtocol).onFiatPaymentStarted();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean isFailedState() {
|
|
||||||
return tradeState == TradeState.BuyerState.FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void setFailedState() {
|
|
||||||
TradeState tradeState = TradeState.BuyerState.FAILED;
|
|
||||||
// We store the phase of the last state into the failed state
|
|
||||||
tradeState.setPhase(tradeState.getPhase());
|
|
||||||
setTradeState(tradeState);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Setter for Mutable objects
|
// Setter for Mutable objects
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTradeState(TradeState tradeState) {
|
public void setState(State state) {
|
||||||
super.setTradeState(tradeState);
|
super.setState(state);
|
||||||
|
|
||||||
switch ((TradeState.BuyerState) tradeState) {
|
if (state == State.WITHDRAW_COMPLETED)
|
||||||
case PREPARATION:
|
tradeProtocol.completed();
|
||||||
break;
|
|
||||||
|
|
||||||
case DEPOSIT_PUBLISHED:
|
|
||||||
takeOfferDate = new Date();
|
|
||||||
|
|
||||||
if (this instanceof OffererTrade)
|
|
||||||
openOfferManager.closeOpenOffer(getOffer());
|
|
||||||
break;
|
|
||||||
case DEPOSIT_PUBLISHED_MSG_SENT:
|
|
||||||
break;
|
|
||||||
case DEPOSIT_CONFIRMED:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FIAT_PAYMENT_STARTED:
|
|
||||||
break;
|
|
||||||
case FIAT_PAYMENT_STARTED_MSG_SENT:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FIAT_PAYMENT_RECEIPT_MSG_RECEIVED:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PAYOUT_TX_COMMITTED:
|
|
||||||
break;
|
|
||||||
case PAYOUT_TX_SENT:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PAYOUT_BROAD_CASTED:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WITHDRAW_COMPLETED:
|
|
||||||
disposeProtocol();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FAILED:
|
|
||||||
disposeProtocol();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
log.error("Unhandled state " + tradeState);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Protected
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void handleConfidenceResult() {
|
|
||||||
if (((TradeState.BuyerState) tradeState).ordinal() < TradeState.BuyerState.DEPOSIT_CONFIRMED.ordinal())
|
|
||||||
setTradeState(TradeState.BuyerState.DEPOSIT_CONFIRMED);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -18,64 +18,232 @@
|
|||||||
package io.bitsquare.trade;
|
package io.bitsquare.trade;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.crypto.PubKeyRing;
|
import io.bitsquare.common.crypto.PubKeyRing;
|
||||||
import io.bitsquare.fiat.FiatAccount;
|
import io.bitsquare.common.util.JsonExclude;
|
||||||
|
import io.bitsquare.p2p.Address;
|
||||||
|
import io.bitsquare.payment.PaymentAccountContractData;
|
||||||
import io.bitsquare.trade.offer.Offer;
|
import io.bitsquare.trade.offer.Offer;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
|
||||||
import java.io.Serializable;
|
|
||||||
|
|
||||||
import javax.annotation.concurrent.Immutable;
|
import javax.annotation.concurrent.Immutable;
|
||||||
|
import java.io.Serializable;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
@SuppressWarnings("WeakerAccess")
|
@SuppressWarnings("WeakerAccess")
|
||||||
@Immutable
|
@Immutable
|
||||||
public class Contract implements Serializable {
|
public class Contract implements Serializable {
|
||||||
// That object is sent over the wire, so we need to take care of version compatibility.
|
// That object is sent over the wire, so we need to take care of version compatibility.
|
||||||
private static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
|
@JsonExclude
|
||||||
|
public static final long serialVersionUID = Version.NETWORK_PROTOCOL_VERSION;
|
||||||
|
|
||||||
public final Offer offer;
|
public final Offer offer;
|
||||||
|
private final long tradeAmount;
|
||||||
public final String takeOfferFeeTxID;
|
public final String takeOfferFeeTxID;
|
||||||
public final Coin tradeAmount;
|
public final Address arbitratorAddress;
|
||||||
public final String buyerAccountID;
|
private final boolean isBuyerOffererOrSellerTaker;
|
||||||
public final String sellerAccountID;
|
private final String offererAccountId;
|
||||||
public final FiatAccount buyerFiatAccount;
|
private final String takerAccountId;
|
||||||
public final FiatAccount sellerFiatAccount;
|
private final PaymentAccountContractData offererPaymentAccountContractData;
|
||||||
public final String buyerP2pSigPubKeyAsString;
|
private final PaymentAccountContractData takerPaymentAccountContractData;
|
||||||
public final String sellerP2pSigPubKeyAsString;
|
@JsonExclude
|
||||||
|
private final PubKeyRing offererPubKeyRing;
|
||||||
|
@JsonExclude
|
||||||
|
private final PubKeyRing takerPubKeyRing;
|
||||||
|
private final Address buyerAddress;
|
||||||
|
private final Address sellerAddress;
|
||||||
|
|
||||||
|
|
||||||
|
private final String offererPayoutAddressString;
|
||||||
|
private final String takerPayoutAddressString;
|
||||||
|
@JsonExclude
|
||||||
|
private final byte[] offererBtcPubKey;
|
||||||
|
@JsonExclude
|
||||||
|
private final byte[] takerBtcPubKey;
|
||||||
|
|
||||||
|
// TODO some basic TAC
|
||||||
|
public final String tac = "With my signature I commit to the trading agreement of Bitsquare and to fulfill the trade as defined there.";
|
||||||
|
|
||||||
|
|
||||||
public Contract(Offer offer,
|
public Contract(Offer offer,
|
||||||
Coin tradeAmount,
|
Coin tradeAmount,
|
||||||
String takeOfferFeeTxID,
|
String takeOfferFeeTxID,
|
||||||
String buyerAccountID,
|
Address buyerAddress,
|
||||||
String sellerAccountID,
|
Address sellerAddress,
|
||||||
FiatAccount buyerFiatAccount,
|
Address arbitratorAddress,
|
||||||
FiatAccount sellerFiatAccount,
|
boolean isBuyerOffererOrSellerTaker,
|
||||||
PubKeyRing buyerPubKeyRing,
|
String offererAccountId,
|
||||||
PubKeyRing sellerPubKeyRing) {
|
String takerAccountId,
|
||||||
|
PaymentAccountContractData offererPaymentAccountContractData,
|
||||||
|
PaymentAccountContractData takerPaymentAccountContractData,
|
||||||
|
PubKeyRing offererPubKeyRing,
|
||||||
|
PubKeyRing takerPubKeyRing,
|
||||||
|
String offererPayoutAddressString,
|
||||||
|
String takerPayoutAddressString,
|
||||||
|
byte[] offererBtcPubKey,
|
||||||
|
byte[] takerBtcPubKey) {
|
||||||
this.offer = offer;
|
this.offer = offer;
|
||||||
this.tradeAmount = tradeAmount;
|
this.buyerAddress = buyerAddress;
|
||||||
|
this.sellerAddress = sellerAddress;
|
||||||
|
this.tradeAmount = tradeAmount.value;
|
||||||
this.takeOfferFeeTxID = takeOfferFeeTxID;
|
this.takeOfferFeeTxID = takeOfferFeeTxID;
|
||||||
this.buyerAccountID = buyerAccountID;
|
this.arbitratorAddress = arbitratorAddress;
|
||||||
this.sellerAccountID = sellerAccountID;
|
this.isBuyerOffererOrSellerTaker = isBuyerOffererOrSellerTaker;
|
||||||
this.buyerFiatAccount = buyerFiatAccount;
|
this.offererAccountId = offererAccountId;
|
||||||
this.sellerFiatAccount = sellerFiatAccount;
|
this.takerAccountId = takerAccountId;
|
||||||
this.buyerP2pSigPubKeyAsString = buyerPubKeyRing.toString();
|
this.offererPaymentAccountContractData = offererPaymentAccountContractData;
|
||||||
this.sellerP2pSigPubKeyAsString = sellerPubKeyRing.toString();
|
this.takerPaymentAccountContractData = takerPaymentAccountContractData;
|
||||||
|
this.offererPubKeyRing = offererPubKeyRing;
|
||||||
|
this.takerPubKeyRing = takerPubKeyRing;
|
||||||
|
this.offererPayoutAddressString = offererPayoutAddressString;
|
||||||
|
this.takerPayoutAddressString = takerPayoutAddressString;
|
||||||
|
this.offererBtcPubKey = offererBtcPubKey;
|
||||||
|
this.takerBtcPubKey = takerBtcPubKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getBuyerAccountId() {
|
||||||
|
return isBuyerOffererOrSellerTaker ? offererAccountId : takerAccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSellerAccountId() {
|
||||||
|
return isBuyerOffererOrSellerTaker ? takerAccountId : offererAccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public String getBuyerPayoutAddressString() {
|
||||||
|
return isBuyerOffererOrSellerTaker ? offererPayoutAddressString : takerPayoutAddressString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getSellerPayoutAddressString() {
|
||||||
|
return isBuyerOffererOrSellerTaker ? takerPayoutAddressString : offererPayoutAddressString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PubKeyRing getBuyerPubKeyRing() {
|
||||||
|
return isBuyerOffererOrSellerTaker ? offererPubKeyRing : takerPubKeyRing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PubKeyRing getSellerPubKeyRing() {
|
||||||
|
return isBuyerOffererOrSellerTaker ? takerPubKeyRing : offererPubKeyRing;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getBuyerBtcPubKey() {
|
||||||
|
return isBuyerOffererOrSellerTaker ? offererBtcPubKey : takerBtcPubKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getSellerBtcPubKey() {
|
||||||
|
return isBuyerOffererOrSellerTaker ? takerBtcPubKey : offererBtcPubKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PaymentAccountContractData getBuyerPaymentAccountContractData() {
|
||||||
|
return isBuyerOffererOrSellerTaker ? offererPaymentAccountContractData : takerPaymentAccountContractData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PaymentAccountContractData getSellerPaymentAccountContractData() {
|
||||||
|
return isBuyerOffererOrSellerTaker ? takerPaymentAccountContractData : offererPaymentAccountContractData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getPaymentMethodName() {
|
||||||
|
// PaymentMethod need to be the same
|
||||||
|
checkArgument(offererPaymentAccountContractData.getPaymentMethodName().equals(takerPaymentAccountContractData.getPaymentMethodName()),
|
||||||
|
"NOT offererPaymentAccountContractData.getPaymentMethodName().equals(takerPaymentAccountContractData.getPaymentMethodName())");
|
||||||
|
return offererPaymentAccountContractData.getPaymentMethodName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Coin getTradeAmount() {
|
||||||
|
return Coin.valueOf(tradeAmount);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Address getBuyerAddress() {
|
||||||
|
return buyerAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Address getSellerAddress() {
|
||||||
|
return sellerAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "Contract{" +
|
return "Contract{" +
|
||||||
"offer=" + offer +
|
"tac='" + tac + '\'' +
|
||||||
", takeOfferFeeTxID='" + takeOfferFeeTxID + '\'' +
|
", offer=" + offer +
|
||||||
", tradeAmount=" + tradeAmount +
|
", tradeAmount=" + tradeAmount +
|
||||||
", buyerAccountID='" + buyerAccountID + '\'' +
|
", isBuyerOffererOrSellerTaker=" + isBuyerOffererOrSellerTaker +
|
||||||
", sellerAccountID='" + sellerAccountID + '\'' +
|
", takeOfferFeeTxID='" + takeOfferFeeTxID + '\'' +
|
||||||
", buyerFiatAccount=" + buyerFiatAccount +
|
", offererAccountID='" + offererAccountId + '\'' +
|
||||||
", sellerFiatAccount=" + sellerFiatAccount +
|
", takerAccountID='" + takerAccountId + '\'' +
|
||||||
", buyerP2pSigPubKeyAsString='" + buyerP2pSigPubKeyAsString + '\'' +
|
", offererPaymentAccount=" + offererPaymentAccountContractData +
|
||||||
", sellerP2pSigPubKeyAsString='" + sellerP2pSigPubKeyAsString + '\'' +
|
", takerPaymentAccount=" + takerPaymentAccountContractData +
|
||||||
|
", offererPubKeyRing=" + offererPubKeyRing +
|
||||||
|
", takerPubKeyRing=" + takerPubKeyRing +
|
||||||
|
", offererPayoutAddressString='" + offererPayoutAddressString + '\'' +
|
||||||
|
", takerPayoutAddressString='" + takerPayoutAddressString + '\'' +
|
||||||
|
", offererBtcPubKey=" + Arrays.toString(offererBtcPubKey) +
|
||||||
|
", takerBtcPubKey=" + Arrays.toString(takerBtcPubKey) +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
if (this == o) return true;
|
||||||
|
if (!(o instanceof Contract)) return false;
|
||||||
|
|
||||||
|
Contract contract = (Contract) o;
|
||||||
|
|
||||||
|
if (tradeAmount != contract.tradeAmount) return false;
|
||||||
|
if (isBuyerOffererOrSellerTaker != contract.isBuyerOffererOrSellerTaker) return false;
|
||||||
|
if (offer != null ? !offer.equals(contract.offer) : contract.offer != null) return false;
|
||||||
|
if (takeOfferFeeTxID != null ? !takeOfferFeeTxID.equals(contract.takeOfferFeeTxID) : contract.takeOfferFeeTxID != null)
|
||||||
|
return false;
|
||||||
|
if (arbitratorAddress != null ? !arbitratorAddress.equals(contract.arbitratorAddress) : contract.arbitratorAddress != null)
|
||||||
|
return false;
|
||||||
|
if (offererAccountId != null ? !offererAccountId.equals(contract.offererAccountId) : contract.offererAccountId != null)
|
||||||
|
return false;
|
||||||
|
if (takerAccountId != null ? !takerAccountId.equals(contract.takerAccountId) : contract.takerAccountId != null)
|
||||||
|
return false;
|
||||||
|
if (offererPaymentAccountContractData != null ? !offererPaymentAccountContractData.equals(contract.offererPaymentAccountContractData) : contract.offererPaymentAccountContractData != null)
|
||||||
|
return false;
|
||||||
|
if (takerPaymentAccountContractData != null ? !takerPaymentAccountContractData.equals(contract.takerPaymentAccountContractData) : contract.takerPaymentAccountContractData != null)
|
||||||
|
return false;
|
||||||
|
if (offererPubKeyRing != null ? !offererPubKeyRing.equals(contract.offererPubKeyRing) : contract.offererPubKeyRing != null)
|
||||||
|
return false;
|
||||||
|
if (takerPubKeyRing != null ? !takerPubKeyRing.equals(contract.takerPubKeyRing) : contract.takerPubKeyRing != null)
|
||||||
|
return false;
|
||||||
|
if (buyerAddress != null ? !buyerAddress.equals(contract.buyerAddress) : contract.buyerAddress != null)
|
||||||
|
return false;
|
||||||
|
if (sellerAddress != null ? !sellerAddress.equals(contract.sellerAddress) : contract.sellerAddress != null)
|
||||||
|
return false;
|
||||||
|
if (offererPayoutAddressString != null ? !offererPayoutAddressString.equals(contract.offererPayoutAddressString) : contract.offererPayoutAddressString != null)
|
||||||
|
return false;
|
||||||
|
if (takerPayoutAddressString != null ? !takerPayoutAddressString.equals(contract.takerPayoutAddressString) : contract.takerPayoutAddressString != null)
|
||||||
|
return false;
|
||||||
|
if (!Arrays.equals(offererBtcPubKey, contract.offererBtcPubKey)) return false;
|
||||||
|
if (!Arrays.equals(takerBtcPubKey, contract.takerBtcPubKey)) return false;
|
||||||
|
return !(tac != null ? !tac.equals(contract.tac) : contract.tac != null);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int hashCode() {
|
||||||
|
int result = offer != null ? offer.hashCode() : 0;
|
||||||
|
result = 31 * result + (int) (tradeAmount ^ (tradeAmount >>> 32));
|
||||||
|
result = 31 * result + (takeOfferFeeTxID != null ? takeOfferFeeTxID.hashCode() : 0);
|
||||||
|
result = 31 * result + (arbitratorAddress != null ? arbitratorAddress.hashCode() : 0);
|
||||||
|
result = 31 * result + (isBuyerOffererOrSellerTaker ? 1 : 0);
|
||||||
|
result = 31 * result + (offererAccountId != null ? offererAccountId.hashCode() : 0);
|
||||||
|
result = 31 * result + (takerAccountId != null ? takerAccountId.hashCode() : 0);
|
||||||
|
result = 31 * result + (offererPaymentAccountContractData != null ? offererPaymentAccountContractData.hashCode() : 0);
|
||||||
|
result = 31 * result + (takerPaymentAccountContractData != null ? takerPaymentAccountContractData.hashCode() : 0);
|
||||||
|
result = 31 * result + (offererPubKeyRing != null ? offererPubKeyRing.hashCode() : 0);
|
||||||
|
result = 31 * result + (takerPubKeyRing != null ? takerPubKeyRing.hashCode() : 0);
|
||||||
|
result = 31 * result + (buyerAddress != null ? buyerAddress.hashCode() : 0);
|
||||||
|
result = 31 * result + (sellerAddress != null ? sellerAddress.hashCode() : 0);
|
||||||
|
result = 31 * result + (offererPayoutAddressString != null ? offererPayoutAddressString.hashCode() : 0);
|
||||||
|
result = 31 * result + (takerPayoutAddressString != null ? takerPayoutAddressString.hashCode() : 0);
|
||||||
|
result = 31 * result + (offererBtcPubKey != null ? Arrays.hashCode(offererBtcPubKey) : 0);
|
||||||
|
result = 31 * result + (takerBtcPubKey != null ? Arrays.hashCode(takerBtcPubKey) : 0);
|
||||||
|
result = 31 * result + (tac != null ? tac.hashCode() : 0);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,9 +17,10 @@
|
|||||||
|
|
||||||
package io.bitsquare.trade;
|
package io.bitsquare.trade;
|
||||||
|
|
||||||
import io.bitsquare.p2p.Peer;
|
|
||||||
|
import io.bitsquare.p2p.Address;
|
||||||
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
|
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
|
||||||
|
|
||||||
public interface OffererTrade {
|
public interface OffererTrade {
|
||||||
void handleTakeOfferRequest(TradeMessage message, Peer taker);
|
void handleTakeOfferRequest(TradeMessage message, Address peerAddress);
|
||||||
}
|
}
|
||||||
|
@ -18,20 +18,19 @@
|
|||||||
package io.bitsquare.trade;
|
package io.bitsquare.trade;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.p2p.Peer;
|
import io.bitsquare.p2p.Address;
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.trade.offer.Offer;
|
import io.bitsquare.trade.offer.Offer;
|
||||||
import io.bitsquare.trade.protocol.trade.OffererProtocol;
|
import io.bitsquare.trade.protocol.trade.OffererProtocol;
|
||||||
import io.bitsquare.trade.protocol.trade.SellerAsOffererProtocol;
|
import io.bitsquare.trade.protocol.trade.SellerAsOffererProtocol;
|
||||||
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
|
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class SellerAsOffererTrade extends SellerTrade implements OffererTrade, Serializable {
|
public class SellerAsOffererTrade extends SellerTrade implements OffererTrade, Serializable {
|
||||||
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
||||||
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
|
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
|
||||||
@ -48,10 +47,13 @@ public class SellerAsOffererTrade extends SellerTrade implements OffererTrade, S
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||||
in.defaultReadObject();
|
try {
|
||||||
|
in.defaultReadObject();
|
||||||
initStateProperties();
|
initStateProperties();
|
||||||
initAmountProperty();
|
initAmountProperty();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
log.trace("Cannot be deserialized." + t.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -65,7 +67,7 @@ public class SellerAsOffererTrade extends SellerTrade implements OffererTrade, S
|
|||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void handleTakeOfferRequest(TradeMessage message, Peer taker) {
|
public void handleTakeOfferRequest(TradeMessage message, Address taker) {
|
||||||
((OffererProtocol) tradeProtocol).handleTakeOfferRequest(message, taker);
|
((OffererProtocol) tradeProtocol).handleTakeOfferRequest(message, taker);
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -18,19 +18,19 @@
|
|||||||
package io.bitsquare.trade;
|
package io.bitsquare.trade;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.p2p.Peer;
|
import io.bitsquare.p2p.Address;
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.trade.offer.Offer;
|
import io.bitsquare.trade.offer.Offer;
|
||||||
import io.bitsquare.trade.protocol.trade.SellerAsTakerProtocol;
|
import io.bitsquare.trade.protocol.trade.SellerAsTakerProtocol;
|
||||||
import io.bitsquare.trade.protocol.trade.TakerProtocol;
|
import io.bitsquare.trade.protocol.trade.TakerProtocol;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class SellerAsTakerTrade extends SellerTrade implements TakerTrade, Serializable {
|
public class SellerAsTakerTrade extends SellerTrade implements TakerTrade, Serializable {
|
||||||
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
||||||
@ -43,15 +43,18 @@ public class SellerAsTakerTrade extends SellerTrade implements TakerTrade, Seria
|
|||||||
// Constructor, initialization
|
// Constructor, initialization
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public SellerAsTakerTrade(Offer offer, Coin tradeAmount, Peer tradingPeer, Storage<? extends TradableList> storage) {
|
public SellerAsTakerTrade(Offer offer, Coin tradeAmount, Address tradingPeerAddress, Storage<? extends TradableList> storage) {
|
||||||
super(offer, tradeAmount, tradingPeer, storage);
|
super(offer, tradeAmount, tradingPeerAddress, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
|
private void readObject(java.io.ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||||
in.defaultReadObject();
|
try {
|
||||||
|
in.defaultReadObject();
|
||||||
initStateProperties();
|
initStateProperties();
|
||||||
initAmountProperty();
|
initAmountProperty();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
log.trace("Cannot be deserialized." + t.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -66,7 +69,7 @@ public class SellerAsTakerTrade extends SellerTrade implements TakerTrade, Seria
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void takeAvailableOffer() {
|
public void takeAvailableOffer() {
|
||||||
assert tradeProtocol instanceof TakerProtocol;
|
checkArgument(tradeProtocol instanceof TakerProtocol, "tradeProtocol NOT instanceof TakerProtocol");
|
||||||
((TakerProtocol) tradeProtocol).takeAvailableOffer();
|
((TakerProtocol) tradeProtocol).takeAvailableOffer();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,19 +18,17 @@
|
|||||||
package io.bitsquare.trade;
|
package io.bitsquare.trade;
|
||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.p2p.Peer;
|
import io.bitsquare.p2p.Address;
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.trade.offer.Offer;
|
import io.bitsquare.trade.offer.Offer;
|
||||||
import io.bitsquare.trade.protocol.trade.SellerProtocol;
|
import io.bitsquare.trade.protocol.trade.SellerProtocol;
|
||||||
|
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import java.util.Date;
|
import static com.google.common.base.Preconditions.checkArgument;
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public abstract class SellerTrade extends Trade implements Serializable {
|
public abstract class SellerTrade extends Trade implements Serializable {
|
||||||
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
||||||
@ -38,8 +36,8 @@ public abstract class SellerTrade extends Trade implements Serializable {
|
|||||||
|
|
||||||
transient private static final Logger log = LoggerFactory.getLogger(BuyerAsTakerTrade.class);
|
transient private static final Logger log = LoggerFactory.getLogger(BuyerAsTakerTrade.class);
|
||||||
|
|
||||||
public SellerTrade(Offer offer, Coin tradeAmount, Peer tradingPeer, Storage<? extends TradableList> storage) {
|
public SellerTrade(Offer offer, Coin tradeAmount, Address tradingPeerAddress, Storage<? extends TradableList> storage) {
|
||||||
super(offer, tradeAmount, tradingPeer, storage);
|
super(offer, tradeAmount, tradingPeerAddress, storage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public SellerTrade(Offer offer, Storage<? extends TradableList> storage) {
|
public SellerTrade(Offer offer, Storage<? extends TradableList> storage) {
|
||||||
@ -48,88 +46,27 @@ public abstract class SellerTrade extends Trade implements Serializable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void initStates() {
|
protected void initStates() {
|
||||||
if (tradeState == null)
|
if (state == null)
|
||||||
tradeState = TradeState.SellerState.PREPARATION;
|
state = State.PREPARATION;
|
||||||
initStateProperties();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void onFiatPaymentReceived() {
|
public void onFiatPaymentReceived() {
|
||||||
assert tradeProtocol instanceof SellerProtocol;
|
checkArgument(tradeProtocol instanceof SellerProtocol, "tradeProtocol NOT instanceof SellerProtocol");
|
||||||
((SellerProtocol) tradeProtocol).onFiatPaymentReceived();
|
((SellerProtocol) tradeProtocol).onFiatPaymentReceived();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public boolean isFailedState() {
|
|
||||||
return tradeState == TradeState.SellerState.FAILED;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setFailedState() {
|
|
||||||
TradeState tradeState = TradeState.SellerState.FAILED;
|
|
||||||
// We store the phase of the last state into the failed state
|
|
||||||
tradeState.setPhase(tradeState.getPhase());
|
|
||||||
setTradeState(tradeState);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Setter for Mutable objects
|
// Setter for Mutable objects
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setTradeState(TradeState tradeState) {
|
public void setState(State state) {
|
||||||
super.setTradeState(tradeState);
|
super.setState(state);
|
||||||
|
|
||||||
switch ((TradeState.SellerState) tradeState) {
|
if (state == State.WITHDRAW_COMPLETED)
|
||||||
|
tradeProtocol.completed();
|
||||||
case PREPARATION:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DEPOSIT_PUBLISHED_MSG_RECEIVED:
|
|
||||||
takeOfferDate = new Date();
|
|
||||||
if (this instanceof OffererTrade)
|
|
||||||
openOfferManager.closeOpenOffer(getOffer());
|
|
||||||
break;
|
|
||||||
case DEPOSIT_CONFIRMED:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FIAT_PAYMENT_STARTED_MSG_RECEIVED:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FIAT_PAYMENT_RECEIPT:
|
|
||||||
break;
|
|
||||||
case FIAT_PAYMENT_RECEIPT_MSG_SENT:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PAYOUT_TX_RECEIVED:
|
|
||||||
break;
|
|
||||||
case PAYOUT_TX_COMMITTED:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case PAYOUT_BROAD_CASTED:
|
|
||||||
break;
|
|
||||||
|
|
||||||
case WITHDRAW_COMPLETED:
|
|
||||||
disposeProtocol();
|
|
||||||
break;
|
|
||||||
|
|
||||||
case FAILED:
|
|
||||||
disposeProtocol();
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
log.error("Unhandled state " + tradeState);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
// Protected
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void handleConfidenceResult() {
|
|
||||||
if (((TradeState.SellerState) tradeState).ordinal() < TradeState.SellerState.DEPOSIT_CONFIRMED.ordinal())
|
|
||||||
setTradeState(TradeState.SellerState.DEPOSIT_CONFIRMED);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,6 @@ package io.bitsquare.trade;
|
|||||||
import io.bitsquare.trade.offer.Offer;
|
import io.bitsquare.trade.offer.Offer;
|
||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
public interface Tradable extends Serializable {
|
public interface Tradable extends Serializable {
|
||||||
@ -29,4 +28,6 @@ public interface Tradable extends Serializable {
|
|||||||
Date getDate();
|
Date getDate();
|
||||||
|
|
||||||
String getId();
|
String getId();
|
||||||
|
|
||||||
|
String getShortId();
|
||||||
}
|
}
|
||||||
|
@ -19,19 +19,16 @@ package io.bitsquare.trade;
|
|||||||
|
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
|
import javafx.collections.FXCollections;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
import javafx.collections.FXCollections;
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
public class TradableList<T extends Tradable> extends ArrayList<T> implements Serializable {
|
public class TradableList<T extends Tradable> extends ArrayList<T> implements Serializable {
|
||||||
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
// That object is saved to disc. We need to take care of changes to not break deserialization.
|
||||||
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
|
private static final long serialVersionUID = Version.LOCAL_DB_VERSION;
|
||||||
@ -59,7 +56,11 @@ public class TradableList<T extends Tradable> extends ArrayList<T> implements Se
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||||
in.defaultReadObject();
|
try {
|
||||||
|
in.defaultReadObject();
|
||||||
|
} catch (Throwable t) {
|
||||||
|
log.trace("Cannot be deserialized." + t.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -17,52 +17,41 @@
|
|||||||
|
|
||||||
package io.bitsquare.trade;
|
package io.bitsquare.trade;
|
||||||
|
|
||||||
|
import com.google.common.base.Throwables;
|
||||||
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
import com.google.common.util.concurrent.Futures;
|
||||||
|
import com.google.common.util.concurrent.ListenableFuture;
|
||||||
import io.bitsquare.app.Version;
|
import io.bitsquare.app.Version;
|
||||||
import io.bitsquare.arbitration.ArbitrationRepository;
|
import io.bitsquare.arbitration.ArbitratorManager;
|
||||||
import io.bitsquare.btc.BlockChainService;
|
import io.bitsquare.btc.FeePolicy;
|
||||||
import io.bitsquare.btc.TradeWalletService;
|
import io.bitsquare.btc.TradeWalletService;
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
|
import io.bitsquare.common.crypto.KeyRing;
|
||||||
import io.bitsquare.common.taskrunner.Model;
|
import io.bitsquare.common.taskrunner.Model;
|
||||||
import io.bitsquare.crypto.CryptoService;
|
import io.bitsquare.p2p.Address;
|
||||||
import io.bitsquare.crypto.KeyRing;
|
import io.bitsquare.p2p.P2PService;
|
||||||
import io.bitsquare.crypto.MessageWithPubKey;
|
import io.bitsquare.p2p.messaging.DecryptedMessageWithPubKey;
|
||||||
import io.bitsquare.p2p.AddressService;
|
|
||||||
import io.bitsquare.p2p.MessageService;
|
|
||||||
import io.bitsquare.p2p.Peer;
|
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.trade.offer.Offer;
|
import io.bitsquare.trade.offer.Offer;
|
||||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||||
import io.bitsquare.trade.protocol.trade.ProcessModel;
|
import io.bitsquare.trade.protocol.trade.ProcessModel;
|
||||||
import io.bitsquare.trade.protocol.trade.TradeProtocol;
|
import io.bitsquare.trade.protocol.trade.TradeProtocol;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
import javafx.beans.property.*;
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
import org.bitcoinj.core.Transaction;
|
import org.bitcoinj.core.Transaction;
|
||||||
import org.bitcoinj.core.TransactionConfidence;
|
import org.bitcoinj.core.TransactionConfidence;
|
||||||
import org.bitcoinj.utils.Fiat;
|
import org.bitcoinj.utils.Fiat;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import com.google.common.base.Throwables;
|
import javax.annotation.Nullable;
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
|
||||||
import com.google.common.util.concurrent.Futures;
|
|
||||||
import com.google.common.util.concurrent.ListenableFuture;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.ObjectInputStream;
|
import java.io.ObjectInputStream;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
|
||||||
|
|
||||||
import javafx.beans.property.ObjectProperty;
|
|
||||||
import javafx.beans.property.ReadOnlyObjectProperty;
|
|
||||||
import javafx.beans.property.SimpleObjectProperty;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds all data which are relevant to the trade, but not those which are only needed in the trade process as shared data between tasks. Those data are
|
* Holds all data which are relevant to the trade, but not those which are only needed in the trade process as shared data between tasks. Those data are
|
||||||
* stored in the task model.
|
* stored in the task model.
|
||||||
@ -73,20 +62,72 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
|
|
||||||
private transient static final Logger log = LoggerFactory.getLogger(Trade.class);
|
private transient static final Logger log = LoggerFactory.getLogger(Trade.class);
|
||||||
|
|
||||||
/* public enum CriticalPhase {
|
public enum State {
|
||||||
|
PREPARATION(Phase.PREPARATION),
|
||||||
|
|
||||||
|
TAKER_FEE_PAID(Phase.TAKER_FEE_PAID),
|
||||||
|
|
||||||
|
DEPOSIT_PUBLISH_REQUESTED(Phase.DEPOSIT_REQUESTED),
|
||||||
|
DEPOSIT_PUBLISHED(Phase.DEPOSIT_PAID),
|
||||||
|
DEPOSIT_SEEN_IN_NETWORK(Phase.DEPOSIT_PAID), // triggered by balance update, used only in error cases
|
||||||
|
DEPOSIT_PUBLISHED_MSG_SENT(Phase.DEPOSIT_PAID),
|
||||||
|
DEPOSIT_PUBLISHED_MSG_RECEIVED(Phase.DEPOSIT_PAID),
|
||||||
|
DEPOSIT_CONFIRMED(Phase.DEPOSIT_PAID),
|
||||||
|
|
||||||
|
FIAT_PAYMENT_STARTED(Phase.FIAT_SENT),
|
||||||
|
FIAT_PAYMENT_STARTED_MSG_SENT(Phase.FIAT_SENT),
|
||||||
|
FIAT_PAYMENT_STARTED_MSG_RECEIVED(Phase.FIAT_SENT),
|
||||||
|
|
||||||
|
FIAT_PAYMENT_RECEIPT(Phase.FIAT_RECEIVED),
|
||||||
|
FIAT_PAYMENT_RECEIPT_MSG_SENT(Phase.FIAT_RECEIVED),
|
||||||
|
FIAT_PAYMENT_RECEIPT_MSG_RECEIVED(Phase.FIAT_RECEIVED),
|
||||||
|
|
||||||
|
PAYOUT_TX_COMMITTED(Phase.PAYOUT_PAID),
|
||||||
|
PAYOUT_TX_SENT(Phase.PAYOUT_PAID),
|
||||||
|
PAYOUT_TX_RECEIVED(Phase.PAYOUT_PAID),
|
||||||
|
PAYOUT_BROAD_CASTED(Phase.PAYOUT_PAID),
|
||||||
|
|
||||||
|
WITHDRAW_COMPLETED(Phase.WITHDRAWN);
|
||||||
|
|
||||||
|
public Phase getPhase() {
|
||||||
|
return phase;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final Phase phase;
|
||||||
|
|
||||||
|
State(Phase phase) {
|
||||||
|
this.phase = phase;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Phase {
|
||||||
PREPARATION,
|
PREPARATION,
|
||||||
TAKER_FEE_PAID,
|
TAKER_FEE_PAID,
|
||||||
|
DEPOSIT_REQUESTED,
|
||||||
DEPOSIT_PAID,
|
DEPOSIT_PAID,
|
||||||
FIAT_SENT,
|
FIAT_SENT,
|
||||||
FIAT_RECEIVED,
|
FIAT_RECEIVED,
|
||||||
PAYOUT_PAID,
|
PAYOUT_PAID,
|
||||||
WITHDRAWN,
|
WITHDRAWN,
|
||||||
FAILED
|
DISPUTE
|
||||||
}*/
|
}
|
||||||
|
|
||||||
|
public enum DisputeState {
|
||||||
|
NONE,
|
||||||
|
DISPUTE_REQUESTED,
|
||||||
|
DISPUTE_STARTED_BY_PEER,
|
||||||
|
DISPUTE_CLOSED
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum TradePeriodState {
|
||||||
|
NORMAL,
|
||||||
|
HALF_REACHED,
|
||||||
|
TRADE_PERIOD_OVER
|
||||||
|
}
|
||||||
|
|
||||||
// Mutable
|
// Mutable
|
||||||
private Coin tradeAmount;
|
private Coin tradeAmount;
|
||||||
private Peer tradingPeer;
|
private Address tradingPeerAddress;
|
||||||
private transient ObjectProperty<Coin> tradeAmountProperty;
|
private transient ObjectProperty<Coin> tradeAmountProperty;
|
||||||
private transient ObjectProperty<Fiat> tradeVolumeProperty;
|
private transient ObjectProperty<Fiat> tradeVolumeProperty;
|
||||||
|
|
||||||
@ -96,31 +137,41 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Transient/Immutable
|
// Transient/Immutable
|
||||||
private transient ObjectProperty<TradeState> processStateProperty;
|
private transient ObjectProperty<State> processStateProperty;
|
||||||
|
private transient ObjectProperty<DisputeState> disputeStateProperty;
|
||||||
|
private transient ObjectProperty<TradePeriodState> tradePeriodStateProperty;
|
||||||
// Trades are saved in the TradeList
|
// Trades are saved in the TradeList
|
||||||
transient private Storage<? extends TradableList> storage;
|
transient private Storage<? extends TradableList> storage;
|
||||||
transient protected TradeProtocol tradeProtocol;
|
transient protected TradeProtocol tradeProtocol;
|
||||||
|
|
||||||
transient protected TradeManager tradeManager;
|
|
||||||
transient protected OpenOfferManager openOfferManager;
|
|
||||||
|
|
||||||
// Immutable
|
// Immutable
|
||||||
private final Offer offer;
|
private final Offer offer;
|
||||||
private final ProcessModel processModel;
|
private final ProcessModel processModel;
|
||||||
private final Date creationDate;
|
|
||||||
|
|
||||||
// Mutable
|
// Mutable
|
||||||
private MessageWithPubKey messageWithPubKey;
|
private DecryptedMessageWithPubKey decryptedMessageWithPubKey;
|
||||||
protected Date takeOfferDate;
|
private Date takeOfferDate = new Date(0); // in some error cases the date is not set and cause null pointers, so we set a default
|
||||||
protected TradeState tradeState;
|
|
||||||
|
protected State state;
|
||||||
|
private DisputeState disputeState = DisputeState.NONE;
|
||||||
|
private TradePeriodState tradePeriodState = TradePeriodState.NORMAL;
|
||||||
private Transaction depositTx;
|
private Transaction depositTx;
|
||||||
private Contract contract;
|
private Contract contract;
|
||||||
private String contractAsJson;
|
private String contractAsJson;
|
||||||
private String sellerContractSignature;
|
private byte[] contractHash;
|
||||||
private String buyerContractSignature;
|
private String takerContractSignature;
|
||||||
|
private String offererContractSignature;
|
||||||
private Transaction payoutTx;
|
private Transaction payoutTx;
|
||||||
private long lockTime;
|
private long lockTimeAsBlockHeight;
|
||||||
|
private int openDisputeTimeAsBlockHeight;
|
||||||
|
private int checkPaymentTimeAsBlockHeight;
|
||||||
|
private String arbitratorId;
|
||||||
|
private Address arbitratorAddress;
|
||||||
|
private String takerPaymentAccountId;
|
||||||
|
private boolean halfTradePeriodReachedWarningDisplayed;
|
||||||
|
private boolean tradePeriodOverWarningDisplayed;
|
||||||
private String errorMessage;
|
private String errorMessage;
|
||||||
|
transient private StringProperty errorMessageProperty;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -134,55 +185,50 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
processModel = new ProcessModel();
|
processModel = new ProcessModel();
|
||||||
tradeVolumeProperty = new SimpleObjectProperty<>();
|
tradeVolumeProperty = new SimpleObjectProperty<>();
|
||||||
tradeAmountProperty = new SimpleObjectProperty<>();
|
tradeAmountProperty = new SimpleObjectProperty<>();
|
||||||
|
errorMessageProperty = new SimpleStringProperty();
|
||||||
|
|
||||||
initStates();
|
initStates();
|
||||||
initStateProperties();
|
initStateProperties();
|
||||||
|
|
||||||
// That will only be used in case of a canceled open offer trade
|
|
||||||
creationDate = new Date();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// taker
|
// taker
|
||||||
protected Trade(Offer offer, Coin tradeAmount, Peer tradingPeer,
|
protected Trade(Offer offer, Coin tradeAmount, Address tradingPeerAddress,
|
||||||
Storage<? extends TradableList> storage) {
|
Storage<? extends TradableList> storage) {
|
||||||
|
|
||||||
this(offer, storage);
|
this(offer, storage);
|
||||||
this.tradeAmount = tradeAmount;
|
this.tradeAmount = tradeAmount;
|
||||||
this.tradingPeer = tradingPeer;
|
this.tradingPeerAddress = tradingPeerAddress;
|
||||||
tradeAmountProperty.set(tradeAmount);
|
tradeAmountProperty.set(tradeAmount);
|
||||||
tradeVolumeProperty.set(getTradeVolume());
|
tradeVolumeProperty.set(getTradeVolume());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
private void readObject(ObjectInputStream in) throws IOException, ClassNotFoundException {
|
||||||
in.defaultReadObject();
|
try {
|
||||||
|
in.defaultReadObject();
|
||||||
initStateProperties();
|
initStateProperties();
|
||||||
initAmountProperty();
|
initAmountProperty();
|
||||||
|
errorMessageProperty = new SimpleStringProperty(errorMessage);
|
||||||
|
} catch (Throwable t) {
|
||||||
|
log.trace("Cannot be deserialized." + t.getMessage());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void init(MessageService messageService,
|
public void init(P2PService p2PService,
|
||||||
WalletService walletService,
|
WalletService walletService,
|
||||||
AddressService addressService,
|
|
||||||
TradeWalletService tradeWalletService,
|
TradeWalletService tradeWalletService,
|
||||||
BlockChainService blockChainService,
|
ArbitratorManager arbitratorManager,
|
||||||
CryptoService cryptoService,
|
|
||||||
ArbitrationRepository arbitrationRepository,
|
|
||||||
TradeManager tradeManager,
|
TradeManager tradeManager,
|
||||||
OpenOfferManager openOfferManager,
|
OpenOfferManager openOfferManager,
|
||||||
User user,
|
User user,
|
||||||
KeyRing keyRing) {
|
KeyRing keyRing) {
|
||||||
|
|
||||||
this.tradeManager = tradeManager;
|
|
||||||
this.openOfferManager = openOfferManager;
|
|
||||||
|
|
||||||
processModel.onAllServicesInitialized(offer,
|
processModel.onAllServicesInitialized(offer,
|
||||||
messageService,
|
tradeManager,
|
||||||
addressService,
|
openOfferManager,
|
||||||
|
p2PService,
|
||||||
walletService,
|
walletService,
|
||||||
tradeWalletService,
|
tradeWalletService,
|
||||||
blockChainService,
|
arbitratorManager,
|
||||||
cryptoService,
|
|
||||||
arbitrationRepository,
|
|
||||||
user,
|
user,
|
||||||
keyRing);
|
keyRing);
|
||||||
|
|
||||||
@ -190,15 +236,15 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
|
|
||||||
tradeProtocol.checkPayoutTxTimeLock(this);
|
tradeProtocol.checkPayoutTxTimeLock(this);
|
||||||
|
|
||||||
if (messageWithPubKey != null) {
|
if (decryptedMessageWithPubKey != null) {
|
||||||
tradeProtocol.applyMailboxMessage(messageWithPubKey, this);
|
tradeProtocol.applyMailboxMessage(decryptedMessageWithPubKey, this);
|
||||||
// After applied to protocol we remove it
|
|
||||||
messageWithPubKey = null;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initStateProperties() {
|
protected void initStateProperties() {
|
||||||
processStateProperty = new SimpleObjectProperty<>(tradeState);
|
processStateProperty = new SimpleObjectProperty<>(state);
|
||||||
|
disputeStateProperty = new SimpleObjectProperty<>(disputeState);
|
||||||
|
tradePeriodStateProperty = new SimpleObjectProperty<>(tradePeriodState);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void initAmountProperty() {
|
protected void initAmountProperty() {
|
||||||
@ -223,43 +269,72 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void setDepositTx(Transaction tx) {
|
public void setDepositTx(Transaction tx) {
|
||||||
|
log.debug("setDepositTx " + tx);
|
||||||
this.depositTx = tx;
|
this.depositTx = tx;
|
||||||
setupConfidenceListener();
|
setupConfidenceListener();
|
||||||
storage.queueUpForSave();
|
storage.queueUpForSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void disposeProtocol() {
|
@Nullable
|
||||||
if (tradeProtocol != null) {
|
public Transaction getDepositTx() {
|
||||||
tradeProtocol.cleanup();
|
return depositTx;
|
||||||
tradeProtocol = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setMailboxMessage(MessageWithPubKey messageWithPubKey) {
|
public void setMailboxMessage(DecryptedMessageWithPubKey decryptedMessageWithPubKey) {
|
||||||
this.messageWithPubKey = messageWithPubKey;
|
log.trace("setMailboxMessage " + decryptedMessageWithPubKey);
|
||||||
|
this.decryptedMessageWithPubKey = decryptedMessageWithPubKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public DecryptedMessageWithPubKey getMailboxMessage() {
|
||||||
|
return decryptedMessageWithPubKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setStorage(Storage<? extends TradableList> storage) {
|
public void setStorage(Storage<? extends TradableList> storage) {
|
||||||
this.storage = storage;
|
this.storage = storage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTradeState(TradeState tradeState) {
|
|
||||||
this.tradeState = tradeState;
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
processStateProperty.set(tradeState);
|
// States
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public void setState(State state) {
|
||||||
|
this.state = state;
|
||||||
|
processStateProperty.set(state);
|
||||||
storage.queueUpForSave();
|
storage.queueUpForSave();
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract public boolean isFailedState();
|
public void setDisputeState(DisputeState disputeState) {
|
||||||
|
this.disputeState = disputeState;
|
||||||
abstract public void setFailedState();
|
disputeStateProperty.set(disputeState);
|
||||||
|
storage.queueUpForSave();
|
||||||
public boolean isCriticalFault() {
|
|
||||||
return tradeState.getPhase() != null && tradeState.getPhase().ordinal() >= TradeState.Phase.DEPOSIT_PAID.ordinal();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DisputeState getDisputeState() {
|
||||||
|
return disputeState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTradePeriodState(TradePeriodState tradePeriodState) {
|
||||||
|
this.tradePeriodState = tradePeriodState;
|
||||||
|
tradePeriodStateProperty.set(tradePeriodState);
|
||||||
|
storage.queueUpForSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
public TradePeriodState getTradePeriodState() {
|
||||||
|
return tradePeriodState;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTakerFeePaid() {
|
||||||
|
return state.getPhase() != null && state.getPhase().ordinal() >= Phase.TAKER_FEE_PAID.ordinal();
|
||||||
|
}
|
||||||
|
|
||||||
|
public State getState() {
|
||||||
|
return state;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Storage
|
// Model implementation
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// Get called from taskRunner after each completed task
|
// Get called from taskRunner after each completed task
|
||||||
@ -282,22 +357,16 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
return offer.getId();
|
return offer.getId();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getShortId() {
|
||||||
|
return offer.getShortId();
|
||||||
|
}
|
||||||
|
|
||||||
public Offer getOffer() {
|
public Offer getOffer() {
|
||||||
return offer;
|
return offer;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public Transaction getDepositTx() {
|
|
||||||
return depositTx;
|
|
||||||
}
|
|
||||||
|
|
||||||
@NotNull
|
|
||||||
public Coin getSecurityDeposit() {
|
|
||||||
return offer.getSecurityDeposit();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Coin getPayoutAmount() {
|
public Coin getPayoutAmount() {
|
||||||
return getSecurityDeposit();
|
return FeePolicy.SECURITY_DEPOSIT;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ProcessModel getProcessModel() {
|
public ProcessModel getProcessModel() {
|
||||||
@ -312,7 +381,8 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ReadOnlyObjectProperty<? extends TradeState> tradeStateProperty() {
|
|
||||||
|
public ReadOnlyObjectProperty<? extends State> stateProperty() {
|
||||||
return processStateProperty;
|
return processStateProperty;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -325,25 +395,36 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<DisputeState> disputeStateProperty() {
|
||||||
|
return disputeStateProperty;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ReadOnlyObjectProperty<TradePeriodState> getTradePeriodStateProperty() {
|
||||||
|
return tradePeriodStateProperty;
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Getter/Setter for Mutable objects
|
// Getter/Setter for Mutable objects
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public Date getDate() {
|
public Date getDate() {
|
||||||
return takeOfferDate != null ? takeOfferDate : creationDate;
|
return takeOfferDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTakeOfferDate(Date takeOfferDate) {
|
public void setTakeOfferDate(Date takeOfferDate) {
|
||||||
this.takeOfferDate = takeOfferDate;
|
this.takeOfferDate = takeOfferDate;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTradingPeer(Peer tradingPeer) {
|
public void setTradingPeerAddress(Address tradingPeerAddress) {
|
||||||
this.tradingPeer = tradingPeer;
|
if (tradingPeerAddress == null)
|
||||||
|
log.error("tradingPeerAddress=null");
|
||||||
|
else
|
||||||
|
this.tradingPeerAddress = tradingPeerAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public Peer getTradingPeer() {
|
public Address getTradingPeerAddress() {
|
||||||
return tradingPeer;
|
return tradingPeerAddress;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTradeAmount(Coin tradeAmount) {
|
public void setTradeAmount(Coin tradeAmount) {
|
||||||
@ -357,31 +438,46 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
return tradeAmount;
|
return tradeAmount;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setLockTime(long lockTime) {
|
public void setLockTimeAsBlockHeight(long lockTimeAsBlockHeight) {
|
||||||
log.debug("lockTime " + lockTime);
|
this.lockTimeAsBlockHeight = lockTimeAsBlockHeight;
|
||||||
this.lockTime = lockTime;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getLockTime() {
|
public long getLockTimeAsBlockHeight() {
|
||||||
return lockTime;
|
return lockTimeAsBlockHeight;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setSellerContractSignature(String takerSignature) {
|
public int getOpenDisputeTimeAsBlockHeight() {
|
||||||
this.sellerContractSignature = takerSignature;
|
return openDisputeTimeAsBlockHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOpenDisputeTimeAsBlockHeight(int openDisputeTimeAsBlockHeight) {
|
||||||
|
this.openDisputeTimeAsBlockHeight = openDisputeTimeAsBlockHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getCheckPaymentTimeAsBlockHeight() {
|
||||||
|
return checkPaymentTimeAsBlockHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckPaymentTimeAsBlockHeight(int checkPaymentTimeAsBlockHeight) {
|
||||||
|
this.checkPaymentTimeAsBlockHeight = checkPaymentTimeAsBlockHeight;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTakerContractSignature(String takerSignature) {
|
||||||
|
this.takerContractSignature = takerSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getSellerContractSignature() {
|
public String getTakerContractSignature() {
|
||||||
return sellerContractSignature;
|
return takerContractSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBuyerContractSignature(String buyerContractSignature) {
|
public void setOffererContractSignature(String offererContractSignature) {
|
||||||
this.buyerContractSignature = buyerContractSignature;
|
this.offererContractSignature = offererContractSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public String getBuyerContractSignature() {
|
public String getOffererContractSignature() {
|
||||||
return buyerContractSignature;
|
return offererContractSignature;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setContractAsJson(String contractAsJson) {
|
public void setContractAsJson(String contractAsJson) {
|
||||||
@ -414,20 +510,53 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
|
|
||||||
public void setErrorMessage(String errorMessage) {
|
public void setErrorMessage(String errorMessage) {
|
||||||
this.errorMessage = errorMessage;
|
this.errorMessage = errorMessage;
|
||||||
|
errorMessageProperty.set(errorMessage);
|
||||||
if (errorMessage != null && errorMessage.length() > 0) {
|
|
||||||
setFailedState();
|
|
||||||
|
|
||||||
if (isCriticalFault())
|
|
||||||
tradeManager.addTradeToFailedTrades(this);
|
|
||||||
else if (isFailedState())
|
|
||||||
tradeManager.addTradeToClosedTrades(this);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
public ReadOnlyStringProperty errorMessageProperty() {
|
||||||
public String getErrorMessage() {
|
return errorMessageProperty;
|
||||||
return errorMessage;
|
}
|
||||||
|
|
||||||
|
public Address getArbitratorAddress() {
|
||||||
|
return arbitratorAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setArbitratorAddress(Address arbitratorAddress) {
|
||||||
|
this.arbitratorAddress = arbitratorAddress;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTakerPaymentAccountId() {
|
||||||
|
return takerPaymentAccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTakerPaymentAccountId(String takerPaymentAccountId) {
|
||||||
|
this.takerPaymentAccountId = takerPaymentAccountId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHalfTradePeriodReachedWarningDisplayed(boolean halfTradePeriodReachedWarningDisplayed) {
|
||||||
|
this.halfTradePeriodReachedWarningDisplayed = halfTradePeriodReachedWarningDisplayed;
|
||||||
|
storage.queueUpForSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isHalfTradePeriodReachedWarningDisplayed() {
|
||||||
|
return halfTradePeriodReachedWarningDisplayed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTradePeriodOverWarningDisplayed(boolean tradePeriodOverWarningDisplayed) {
|
||||||
|
this.tradePeriodOverWarningDisplayed = tradePeriodOverWarningDisplayed;
|
||||||
|
storage.queueUpForSave();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isTradePeriodOverWarningDisplayed() {
|
||||||
|
return tradePeriodOverWarningDisplayed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setContractHash(byte[] contractHash) {
|
||||||
|
this.contractHash = contractHash;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getContractHash() {
|
||||||
|
return contractHash;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -436,28 +565,42 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
private void setupConfidenceListener() {
|
private void setupConfidenceListener() {
|
||||||
|
log.debug("setupConfidenceListener");
|
||||||
if (depositTx != null) {
|
if (depositTx != null) {
|
||||||
TransactionConfidence transactionConfidence = depositTx.getConfidence();
|
TransactionConfidence transactionConfidence = depositTx.getConfidence();
|
||||||
ListenableFuture<TransactionConfidence> future = transactionConfidence.getDepthFuture(1);
|
log.debug("transactionConfidence " + transactionConfidence.getDepthInBlocks());
|
||||||
Futures.addCallback(future, new FutureCallback<TransactionConfidence>() {
|
if (transactionConfidence.getDepthInBlocks() > 0) {
|
||||||
@Override
|
handleConfidenceResult();
|
||||||
public void onSuccess(TransactionConfidence result) {
|
} else {
|
||||||
handleConfidenceResult();
|
ListenableFuture<TransactionConfidence> future = transactionConfidence.getDepthFuture(1);
|
||||||
}
|
Futures.addCallback(future, new FutureCallback<TransactionConfidence>() {
|
||||||
|
@Override
|
||||||
|
public void onSuccess(TransactionConfidence result) {
|
||||||
|
log.debug("transactionConfidence " + transactionConfidence.getDepthInBlocks());
|
||||||
|
log.debug("state " + state);
|
||||||
|
handleConfidenceResult();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onFailure(@NotNull Throwable t) {
|
public void onFailure(@NotNull Throwable t) {
|
||||||
t.printStackTrace();
|
t.printStackTrace();
|
||||||
log.error(t.getMessage());
|
log.error(t.getMessage());
|
||||||
Throwables.propagate(t);
|
Throwables.propagate(t);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
log.error("depositTx == null. That must not happen.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
abstract protected void createProtocol();
|
abstract protected void createProtocol();
|
||||||
|
|
||||||
abstract protected void handleConfidenceResult();
|
private void handleConfidenceResult() {
|
||||||
|
if (state.ordinal() < State.DEPOSIT_CONFIRMED.ordinal())
|
||||||
|
setState(State.DEPOSIT_CONFIRMED);
|
||||||
|
}
|
||||||
|
|
||||||
abstract protected void initStates();
|
abstract protected void initStates();
|
||||||
|
|
||||||
@ -465,7 +608,7 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
public String toString() {
|
public String toString() {
|
||||||
return "Trade{" +
|
return "Trade{" +
|
||||||
"tradeAmount=" + tradeAmount +
|
"tradeAmount=" + tradeAmount +
|
||||||
", tradingPeer=" + tradingPeer +
|
", tradingPeer=" + tradingPeerAddress +
|
||||||
", tradeAmountProperty=" + tradeAmountProperty +
|
", tradeAmountProperty=" + tradeAmountProperty +
|
||||||
", tradeVolumeProperty=" + tradeVolumeProperty +
|
", tradeVolumeProperty=" + tradeVolumeProperty +
|
||||||
", processStateProperty=" + processStateProperty +
|
", processStateProperty=" + processStateProperty +
|
||||||
@ -474,8 +617,8 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
", offer=" + offer +
|
", offer=" + offer +
|
||||||
", date=" + takeOfferDate +
|
", date=" + takeOfferDate +
|
||||||
", processModel=" + processModel +
|
", processModel=" + processModel +
|
||||||
", processState=" + tradeState +
|
", processState=" + state +
|
||||||
", messageWithPubKey=" + messageWithPubKey +
|
", messageWithPubKey=" + decryptedMessageWithPubKey +
|
||||||
", depositTx=" + depositTx +
|
", depositTx=" + depositTx +
|
||||||
/* ", contract=" + contract +
|
/* ", contract=" + contract +
|
||||||
", contractAsJson='" + contractAsJson + '\'' +*/
|
", contractAsJson='" + contractAsJson + '\'' +*/
|
||||||
@ -485,5 +628,4 @@ abstract public class Trade implements Tradable, Model, Serializable {
|
|||||||
", errorMessage='" + errorMessage + '\'' +
|
", errorMessage='" + errorMessage + '\'' +
|
||||||
'}';
|
'}';
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -17,61 +17,52 @@
|
|||||||
|
|
||||||
package io.bitsquare.trade;
|
package io.bitsquare.trade;
|
||||||
|
|
||||||
import io.bitsquare.arbitration.ArbitrationRepository;
|
import com.google.common.util.concurrent.FutureCallback;
|
||||||
|
import io.bitsquare.arbitration.ArbitratorManager;
|
||||||
import io.bitsquare.btc.AddressEntry;
|
import io.bitsquare.btc.AddressEntry;
|
||||||
import io.bitsquare.btc.BlockChainService;
|
|
||||||
import io.bitsquare.btc.TradeWalletService;
|
import io.bitsquare.btc.TradeWalletService;
|
||||||
import io.bitsquare.btc.WalletService;
|
import io.bitsquare.btc.WalletService;
|
||||||
|
import io.bitsquare.common.crypto.KeyRing;
|
||||||
import io.bitsquare.common.handlers.FaultHandler;
|
import io.bitsquare.common.handlers.FaultHandler;
|
||||||
import io.bitsquare.common.handlers.ResultHandler;
|
import io.bitsquare.common.handlers.ResultHandler;
|
||||||
import io.bitsquare.crypto.CryptoService;
|
import io.bitsquare.p2p.Address;
|
||||||
import io.bitsquare.crypto.KeyRing;
|
|
||||||
import io.bitsquare.crypto.MessageWithPubKey;
|
|
||||||
import io.bitsquare.crypto.SealedAndSignedMessage;
|
|
||||||
import io.bitsquare.p2p.AddressService;
|
|
||||||
import io.bitsquare.p2p.DecryptedMessageHandler;
|
|
||||||
import io.bitsquare.p2p.MailboxMessage;
|
|
||||||
import io.bitsquare.p2p.MailboxService;
|
|
||||||
import io.bitsquare.p2p.Message;
|
import io.bitsquare.p2p.Message;
|
||||||
import io.bitsquare.p2p.MessageService;
|
import io.bitsquare.p2p.P2PService;
|
||||||
import io.bitsquare.p2p.Peer;
|
import io.bitsquare.p2p.P2PServiceListener;
|
||||||
|
import io.bitsquare.p2p.messaging.DecryptedMailListener;
|
||||||
|
import io.bitsquare.p2p.messaging.DecryptedMailboxListener;
|
||||||
|
import io.bitsquare.p2p.messaging.DecryptedMessageWithPubKey;
|
||||||
import io.bitsquare.storage.Storage;
|
import io.bitsquare.storage.Storage;
|
||||||
import io.bitsquare.trade.closed.ClosedTradableManager;
|
import io.bitsquare.trade.closed.ClosedTradableManager;
|
||||||
import io.bitsquare.trade.failed.FailedTradesManager;
|
import io.bitsquare.trade.failed.FailedTradesManager;
|
||||||
import io.bitsquare.trade.handlers.TakeOfferResultHandler;
|
import io.bitsquare.trade.handlers.TradeResultHandler;
|
||||||
import io.bitsquare.trade.offer.Offer;
|
import io.bitsquare.trade.offer.Offer;
|
||||||
import io.bitsquare.trade.offer.OpenOffer;
|
import io.bitsquare.trade.offer.OpenOffer;
|
||||||
import io.bitsquare.trade.offer.OpenOfferManager;
|
import io.bitsquare.trade.offer.OpenOfferManager;
|
||||||
import io.bitsquare.trade.protocol.availability.OfferAvailabilityModel;
|
import io.bitsquare.trade.protocol.availability.OfferAvailabilityModel;
|
||||||
import io.bitsquare.trade.protocol.trade.messages.DepositTxInputsRequest;
|
|
||||||
import io.bitsquare.trade.protocol.trade.messages.PayDepositRequest;
|
import io.bitsquare.trade.protocol.trade.messages.PayDepositRequest;
|
||||||
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
|
import io.bitsquare.trade.protocol.trade.messages.TradeMessage;
|
||||||
import io.bitsquare.user.User;
|
import io.bitsquare.user.User;
|
||||||
|
import javafx.beans.property.BooleanProperty;
|
||||||
|
import javafx.beans.property.SimpleBooleanProperty;
|
||||||
|
import javafx.collections.ObservableList;
|
||||||
import org.bitcoinj.core.AddressFormatException;
|
import org.bitcoinj.core.AddressFormatException;
|
||||||
import org.bitcoinj.core.Coin;
|
import org.bitcoinj.core.Coin;
|
||||||
import org.bitcoinj.core.InsufficientMoneyException;
|
import org.bitcoinj.core.InsufficientMoneyException;
|
||||||
import org.bitcoinj.core.Transaction;
|
import org.bitcoinj.core.Transaction;
|
||||||
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.spongycastle.crypto.params.KeyParameter;
|
||||||
|
|
||||||
import com.google.common.util.concurrent.FutureCallback;
|
import javax.inject.Inject;
|
||||||
|
import javax.inject.Named;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Optional;
|
import java.util.Optional;
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
import javax.inject.Named;
|
|
||||||
|
|
||||||
import javafx.collections.ObservableList;
|
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
|
||||||
import org.slf4j.LoggerFactory;
|
|
||||||
|
|
||||||
import static io.bitsquare.util.Validator.nonEmptyStringOf;
|
import static io.bitsquare.util.Validator.nonEmptyStringOf;
|
||||||
|
|
||||||
public class TradeManager {
|
public class TradeManager {
|
||||||
@ -79,20 +70,18 @@ public class TradeManager {
|
|||||||
|
|
||||||
private final User user;
|
private final User user;
|
||||||
private final KeyRing keyRing;
|
private final KeyRing keyRing;
|
||||||
private final MessageService messageService;
|
|
||||||
private final MailboxService mailboxService;
|
|
||||||
private final AddressService addressService;
|
|
||||||
private final BlockChainService blockChainService;
|
|
||||||
private final WalletService walletService;
|
private final WalletService walletService;
|
||||||
private final TradeWalletService tradeWalletService;
|
private final TradeWalletService tradeWalletService;
|
||||||
private final CryptoService<MailboxMessage> cryptoService;
|
|
||||||
private final OpenOfferManager openOfferManager;
|
private final OpenOfferManager openOfferManager;
|
||||||
private final ClosedTradableManager closedTradableManager;
|
private final ClosedTradableManager closedTradableManager;
|
||||||
private final FailedTradesManager failedTradesManager;
|
private final FailedTradesManager failedTradesManager;
|
||||||
private final ArbitrationRepository arbitrationRepository;
|
private final ArbitratorManager arbitratorManager;
|
||||||
|
private final P2PService p2PService;
|
||||||
|
|
||||||
private final Storage<TradableList<Trade>> pendingTradesStorage;
|
private final Storage<TradableList<Trade>> tradableListStorage;
|
||||||
private final TradableList<Trade> pendingTrades;
|
private final TradableList<Trade> trades;
|
||||||
|
private final BooleanProperty pendingTradesInitialized = new SimpleBooleanProperty();
|
||||||
|
private P2PServiceListener p2PServiceListener;
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
@ -102,34 +91,86 @@ public class TradeManager {
|
|||||||
@Inject
|
@Inject
|
||||||
public TradeManager(User user,
|
public TradeManager(User user,
|
||||||
KeyRing keyRing,
|
KeyRing keyRing,
|
||||||
MessageService messageService,
|
|
||||||
MailboxService mailboxService,
|
|
||||||
AddressService addressService,
|
|
||||||
BlockChainService blockChainService,
|
|
||||||
WalletService walletService,
|
WalletService walletService,
|
||||||
TradeWalletService tradeWalletService,
|
TradeWalletService tradeWalletService,
|
||||||
CryptoService<MailboxMessage> cryptoService,
|
|
||||||
OpenOfferManager openOfferManager,
|
OpenOfferManager openOfferManager,
|
||||||
ClosedTradableManager closedTradableManager,
|
ClosedTradableManager closedTradableManager,
|
||||||
FailedTradesManager failedTradesManager,
|
FailedTradesManager failedTradesManager,
|
||||||
ArbitrationRepository arbitrationRepository,
|
ArbitratorManager arbitratorManager,
|
||||||
|
P2PService p2PService,
|
||||||
@Named("storage.dir") File storageDir) {
|
@Named("storage.dir") File storageDir) {
|
||||||
this.user = user;
|
this.user = user;
|
||||||
this.keyRing = keyRing;
|
this.keyRing = keyRing;
|
||||||
this.messageService = messageService;
|
|
||||||
this.mailboxService = mailboxService;
|
|
||||||
this.addressService = addressService;
|
|
||||||
this.blockChainService = blockChainService;
|
|
||||||
this.walletService = walletService;
|
this.walletService = walletService;
|
||||||
this.tradeWalletService = tradeWalletService;
|
this.tradeWalletService = tradeWalletService;
|
||||||
this.cryptoService = cryptoService;
|
|
||||||
this.openOfferManager = openOfferManager;
|
this.openOfferManager = openOfferManager;
|
||||||
this.closedTradableManager = closedTradableManager;
|
this.closedTradableManager = closedTradableManager;
|
||||||
this.failedTradesManager = failedTradesManager;
|
this.failedTradesManager = failedTradesManager;
|
||||||
this.arbitrationRepository = arbitrationRepository;
|
this.arbitratorManager = arbitratorManager;
|
||||||
|
this.p2PService = p2PService;
|
||||||
|
|
||||||
pendingTradesStorage = new Storage<>(storageDir);
|
tradableListStorage = new Storage<>(storageDir);
|
||||||
this.pendingTrades = new TradableList<>(pendingTradesStorage, "PendingTrades");
|
this.trades = new TradableList<>(tradableListStorage, "PendingTrades");
|
||||||
|
|
||||||
|
p2PService.addDecryptedMailListener(new DecryptedMailListener() {
|
||||||
|
@Override
|
||||||
|
public void onMailMessage(DecryptedMessageWithPubKey decryptedMessageWithPubKey, Address peerAddress) {
|
||||||
|
Message message = decryptedMessageWithPubKey.message;
|
||||||
|
|
||||||
|
// Handler for incoming initial messages from taker
|
||||||
|
if (message instanceof PayDepositRequest) {
|
||||||
|
log.trace("Received PayDepositRequest: " + message);
|
||||||
|
handleInitialTakeOfferRequest((PayDepositRequest) message, peerAddress);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
p2PService.addDecryptedMailboxListener(new DecryptedMailboxListener() {
|
||||||
|
@Override
|
||||||
|
public void onMailboxMessageAdded(DecryptedMessageWithPubKey decryptedMessageWithPubKey, Address senderAddress) {
|
||||||
|
log.trace("onMailboxMessageAdded decryptedMessageWithPubKey: " + decryptedMessageWithPubKey);
|
||||||
|
log.trace("onMailboxMessageAdded senderAddress: " + senderAddress);
|
||||||
|
Message message = decryptedMessageWithPubKey.message;
|
||||||
|
if (message instanceof PayDepositRequest) {
|
||||||
|
//TODO is that used????
|
||||||
|
PayDepositRequest payDepositRequest = (PayDepositRequest) message;
|
||||||
|
log.trace("Received payDepositRequest: " + payDepositRequest);
|
||||||
|
if (payDepositRequest.getSenderAddress().equals(senderAddress))
|
||||||
|
handleInitialTakeOfferRequest(payDepositRequest, senderAddress);
|
||||||
|
else
|
||||||
|
log.warn("Peer address not matching for payDepositRequest");
|
||||||
|
} else if (message instanceof TradeMessage) {
|
||||||
|
log.trace("Received TradeMessage: " + message);
|
||||||
|
String tradeId = ((TradeMessage) message).tradeId;
|
||||||
|
Optional<Trade> tradeOptional = trades.stream().filter(e -> e.getId().equals(tradeId)).findAny();
|
||||||
|
if (tradeOptional.isPresent())
|
||||||
|
tradeOptional.get().setMailboxMessage(decryptedMessageWithPubKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
p2PServiceListener = new P2PServiceListener() {
|
||||||
|
@Override
|
||||||
|
public void onTorNodeReady() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onHiddenServiceReady() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSetupFailed(Throwable throwable) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAllDataReceived() {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthenticated() {
|
||||||
|
initPendingTrades();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
p2PService.addP2PServiceListener(p2PServiceListener);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -141,35 +182,41 @@ public class TradeManager {
|
|||||||
// OffererAsBuyerProtocol listens for take offer requests, so we need to instantiate it early.
|
// OffererAsBuyerProtocol listens for take offer requests, so we need to instantiate it early.
|
||||||
public void onAllServicesInitialized() {
|
public void onAllServicesInitialized() {
|
||||||
log.trace("onAllServicesInitialized");
|
log.trace("onAllServicesInitialized");
|
||||||
|
|
||||||
// If there are messages in our mailbox we apply it and remove them from the DHT
|
|
||||||
// We run that before initializing the pending trades to be sure the state is correct
|
|
||||||
mailboxService.getAllMessages(
|
|
||||||
(encryptedMailboxMessages) -> {
|
|
||||||
log.trace("mailboxService.getAllMessages success");
|
|
||||||
setMailboxMessagesToTrades(encryptedMailboxMessages);
|
|
||||||
emptyMailbox();
|
|
||||||
initPendingTrades();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Handler for incoming initial messages from taker
|
|
||||||
messageService.addDecryptedMessageHandler(new DecryptedMessageHandler() {
|
|
||||||
@Override
|
|
||||||
public void handleMessage(MessageWithPubKey messageWithPubKey, Peer sender) {
|
|
||||||
// We get an encrypted message but don't do the signature check as we don't know the peer yet.
|
|
||||||
// A basic sig check is in done also at decryption time
|
|
||||||
Message message = messageWithPubKey.getMessage();
|
|
||||||
// Those 2 messages are initial request form the taker.
|
|
||||||
// RequestPayDepositMessage is used also in case of SellerAsTaker but there it is handled in the protocol as it is not an initial request
|
|
||||||
if (message instanceof DepositTxInputsRequest ||
|
|
||||||
(message instanceof PayDepositRequest && ((PayDepositRequest) message).isInitialRequest))
|
|
||||||
handleInitialTakeOfferRequest((TradeMessage) message, sender);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleInitialTakeOfferRequest(TradeMessage message, Peer sender) {
|
private void initPendingTrades() {
|
||||||
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + sender);
|
if (p2PServiceListener != null) p2PService.removeP2PServiceListener(p2PServiceListener);
|
||||||
|
|
||||||
|
List<Trade> failedTrades = new ArrayList<>();
|
||||||
|
for (Trade trade : trades) {
|
||||||
|
// We continue an interrupted trade.
|
||||||
|
// TODO if the peer has changed its IP address, we need to make another findPeer request. At the moment we use the peer stored in trade to
|
||||||
|
// continue the trade, but that might fail.
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
/* if (trade.isFailedState()) {
|
||||||
|
failedTrades.add(trade);
|
||||||
|
}
|
||||||
|
else {*/
|
||||||
|
trade.setStorage(tradableListStorage);
|
||||||
|
trade.updateDepositTxFromWallet(tradeWalletService);
|
||||||
|
initTrade(trade);
|
||||||
|
|
||||||
|
// after we are authenticated we remove mailbox messages.
|
||||||
|
DecryptedMessageWithPubKey mailboxMessage = trade.getMailboxMessage();
|
||||||
|
if (mailboxMessage != null) {
|
||||||
|
p2PService.removeEntryFromMailbox(mailboxMessage);
|
||||||
|
trade.setMailboxMessage(null);
|
||||||
|
}
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
pendingTradesInitialized.set(true);
|
||||||
|
|
||||||
|
failedTrades.stream().filter(Trade::isTakerFeePaid).forEach(this::addTradeToFailedTrades);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleInitialTakeOfferRequest(TradeMessage message, Address peerAddress) {
|
||||||
|
log.trace("handleNewMessage: message = " + message.getClass().getSimpleName() + " from " + peerAddress);
|
||||||
try {
|
try {
|
||||||
nonEmptyStringOf(message.tradeId);
|
nonEmptyStringOf(message.tradeId);
|
||||||
} catch (Throwable t) {
|
} catch (Throwable t) {
|
||||||
@ -184,93 +231,35 @@ public class TradeManager {
|
|||||||
|
|
||||||
Trade trade;
|
Trade trade;
|
||||||
if (offer.getDirection() == Offer.Direction.BUY)
|
if (offer.getDirection() == Offer.Direction.BUY)
|
||||||
trade = new BuyerAsOffererTrade(offer, pendingTradesStorage);
|
trade = new BuyerAsOffererTrade(offer, tradableListStorage);
|
||||||
else
|
else
|
||||||
trade = new SellerAsOffererTrade(offer, pendingTradesStorage);
|
trade = new SellerAsOffererTrade(offer, tradableListStorage);
|
||||||
|
|
||||||
trade.setStorage(pendingTradesStorage);
|
trade.setStorage(tradableListStorage);
|
||||||
initTrade(trade);
|
initTrade(trade);
|
||||||
pendingTrades.add(trade);
|
trades.add(trade);
|
||||||
((OffererTrade) trade).handleTakeOfferRequest(message, sender);
|
((OffererTrade) trade).handleTakeOfferRequest(message, peerAddress);
|
||||||
}
|
} else {
|
||||||
else {
|
|
||||||
// TODO respond
|
// TODO respond
|
||||||
//(RequestDepositTxInputsMessage)message.
|
//(RequestDepositTxInputsMessage)message.
|
||||||
// messageService.sendEncryptedMessage(sender,messageWithPubKey.getMessage().);
|
// messageService.sendEncryptedMessage(peerAddress,messageWithPubKey.getMessage().);
|
||||||
log.info("We received a take offer request but don't have that offer anymore.");
|
log.info("We received a take offer request but don't have that offer anymore.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initTrade(Trade trade) {
|
private void initTrade(Trade trade) {
|
||||||
trade.init(messageService,
|
trade.init(p2PService,
|
||||||
walletService,
|
walletService,
|
||||||
addressService,
|
|
||||||
tradeWalletService,
|
tradeWalletService,
|
||||||
blockChainService,
|
arbitratorManager,
|
||||||
cryptoService,
|
|
||||||
arbitrationRepository,
|
|
||||||
this,
|
this,
|
||||||
openOfferManager,
|
openOfferManager,
|
||||||
user,
|
user,
|
||||||
keyRing);
|
keyRing);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setMailboxMessagesToTrades(List<SealedAndSignedMessage> encryptedMessages) {
|
|
||||||
log.trace("applyMailboxMessage encryptedMailboxMessage.size=" + encryptedMessages.size());
|
|
||||||
for (SealedAndSignedMessage encrypted : encryptedMessages) {
|
|
||||||
try {
|
|
||||||
MessageWithPubKey messageWithPubKey = cryptoService.decryptAndVerifyMessage(encrypted);
|
|
||||||
Message message = messageWithPubKey.getMessage();
|
|
||||||
if (message instanceof MailboxMessage && message instanceof TradeMessage) {
|
|
||||||
String tradeId = ((TradeMessage) message).tradeId;
|
|
||||||
Optional<Trade> tradeOptional = pendingTrades.stream().filter(e -> e.getId().equals(tradeId)).findAny();
|
|
||||||
if (tradeOptional.isPresent())
|
|
||||||
tradeOptional.get().setMailboxMessage(messageWithPubKey);
|
|
||||||
}
|
|
||||||
} catch (Throwable e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
log.error(e.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void emptyMailbox() {
|
|
||||||
mailboxService.removeAllMessages(
|
|
||||||
() -> log.debug("All mailbox entries removed"),
|
|
||||||
(errorMessage, fault) -> {
|
|
||||||
log.error(errorMessage);
|
|
||||||
log.error(fault.getMessage());
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initPendingTrades() {
|
|
||||||
List<Trade> failedTrades = new ArrayList<>();
|
|
||||||
for (Trade trade : pendingTrades) {
|
|
||||||
// We continue an interrupted trade.
|
|
||||||
// TODO if the peer has changed its IP address, we need to make another findPeer request. At the moment we use the peer stored in trade to
|
|
||||||
// continue the trade, but that might fail.
|
|
||||||
|
|
||||||
if (trade.isFailedState()) {
|
|
||||||
failedTrades.add(trade);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
trade.setStorage(pendingTradesStorage);
|
|
||||||
trade.updateDepositTxFromWallet(tradeWalletService);
|
|
||||||
initTrade(trade);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for (Trade trade : failedTrades) {
|
|
||||||
if (trade.isCriticalFault())
|
|
||||||
addTradeToFailedTrades(trade);
|
|
||||||
else
|
|
||||||
addTradeToClosedTrades(trade);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Called from Offerbook when offer gets removed from DHT
|
// Called from Offerbook when offer gets removed from P2P network
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void onOfferRemovedFromRemoteOfferBook(Offer offer) {
|
public void onOfferRemovedFromRemoteOfferBook(Offer offer) {
|
||||||
@ -282,8 +271,9 @@ public class TradeManager {
|
|||||||
// Take offer
|
// Take offer
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void onCheckOfferAvailability(Offer offer) {
|
public void checkOfferAvailability(Offer offer,
|
||||||
offer.checkOfferAvailability(getOfferAvailabilityModel(offer));
|
ResultHandler resultHandler) {
|
||||||
|
offer.checkOfferAvailability(getOfferAvailabilityModel(offer), resultHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
// When closing take offer view, we are not interested in the onCheckOfferAvailability result anymore, so remove from the map
|
// When closing take offer view, we are not interested in the onCheckOfferAvailability result anymore, so remove from the map
|
||||||
@ -292,34 +282,44 @@ public class TradeManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// First we check if offer is still available then we create the trade with the protocol
|
// First we check if offer is still available then we create the trade with the protocol
|
||||||
public void onTakeOffer(Coin amount, Offer offer, TakeOfferResultHandler takeOfferResultHandler) {
|
public void onTakeOffer(Coin amount,
|
||||||
|
Offer offer,
|
||||||
|
String paymentAccountId,
|
||||||
|
TradeResultHandler tradeResultHandler) {
|
||||||
final OfferAvailabilityModel model = getOfferAvailabilityModel(offer);
|
final OfferAvailabilityModel model = getOfferAvailabilityModel(offer);
|
||||||
offer.checkOfferAvailability(model, () -> {
|
offer.checkOfferAvailability(model,
|
||||||
if (offer.getState() == Offer.State.AVAILABLE)
|
() -> {
|
||||||
createTrade(amount, offer, model, takeOfferResultHandler);
|
if (offer.getState() == Offer.State.AVAILABLE)
|
||||||
});
|
createTrade(amount, offer, paymentAccountId, model, tradeResultHandler);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createTrade(Coin amount, Offer offer, OfferAvailabilityModel model, TakeOfferResultHandler takeOfferResultHandler) {
|
private void createTrade(Coin amount,
|
||||||
|
Offer offer,
|
||||||
|
String paymentAccountId,
|
||||||
|
OfferAvailabilityModel model,
|
||||||
|
TradeResultHandler tradeResultHandler) {
|
||||||
Trade trade;
|
Trade trade;
|
||||||
if (offer.getDirection() == Offer.Direction.BUY)
|
if (offer.getDirection() == Offer.Direction.BUY)
|
||||||
trade = new SellerAsTakerTrade(offer, amount, model.getPeer(), pendingTradesStorage);
|
trade = new SellerAsTakerTrade(offer, amount, model.getPeerAddress(), tradableListStorage);
|
||||||
else
|
else
|
||||||
trade = new BuyerAsTakerTrade(offer, amount, model.getPeer(), pendingTradesStorage);
|
trade = new BuyerAsTakerTrade(offer, amount, model.getPeerAddress(), tradableListStorage);
|
||||||
|
|
||||||
trade.setTakeOfferDate(new Date());
|
trade.setTakeOfferDate(new Date());
|
||||||
|
trade.setTakerPaymentAccountId(paymentAccountId);
|
||||||
|
|
||||||
initTrade(trade);
|
initTrade(trade);
|
||||||
pendingTrades.add(trade);
|
|
||||||
|
trades.add(trade);
|
||||||
((TakerTrade) trade).takeAvailableOffer();
|
((TakerTrade) trade).takeAvailableOffer();
|
||||||
takeOfferResultHandler.handleResult(trade);
|
tradeResultHandler.handleResult(trade);
|
||||||
}
|
}
|
||||||
|
|
||||||
private OfferAvailabilityModel getOfferAvailabilityModel(Offer offer) {
|
private OfferAvailabilityModel getOfferAvailabilityModel(Offer offer) {
|
||||||
return new OfferAvailabilityModel(
|
return new OfferAvailabilityModel(
|
||||||
offer,
|
offer,
|
||||||
keyRing.getPubKeyRing(),
|
keyRing.getPubKeyRing(),
|
||||||
messageService,
|
p2PService);
|
||||||
addressService);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -327,8 +327,8 @@ public class TradeManager {
|
|||||||
// Trade
|
// Trade
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public void onWithdrawRequest(String toAddress, Trade trade, ResultHandler resultHandler, FaultHandler faultHandler) {
|
public void onWithdrawRequest(String toAddress, KeyParameter aesKey, Trade trade, ResultHandler resultHandler, FaultHandler faultHandler) {
|
||||||
AddressEntry addressEntry = walletService.getAddressEntry(trade.getId());
|
AddressEntry addressEntry = walletService.getAddressEntryByOfferId(trade.getId());
|
||||||
String fromAddress = addressEntry.getAddressString();
|
String fromAddress = addressEntry.getAddressString();
|
||||||
|
|
||||||
FutureCallback<Transaction> callback = new FutureCallback<Transaction>() {
|
FutureCallback<Transaction> callback = new FutureCallback<Transaction>() {
|
||||||
@ -336,15 +336,8 @@ public class TradeManager {
|
|||||||
public void onSuccess(@javax.annotation.Nullable Transaction transaction) {
|
public void onSuccess(@javax.annotation.Nullable Transaction transaction) {
|
||||||
if (transaction != null) {
|
if (transaction != null) {
|
||||||
log.info("onWithdraw onSuccess tx ID:" + transaction.getHashAsString());
|
log.info("onWithdraw onSuccess tx ID:" + transaction.getHashAsString());
|
||||||
|
trade.setState(Trade.State.WITHDRAW_COMPLETED);
|
||||||
if (trade instanceof BuyerTrade)
|
addTradeToClosedTrades(trade);
|
||||||
trade.setTradeState(TradeState.BuyerState.WITHDRAW_COMPLETED);
|
|
||||||
else if (trade instanceof SellerTrade)
|
|
||||||
trade.setTradeState(TradeState.SellerState.WITHDRAW_COMPLETED);
|
|
||||||
|
|
||||||
pendingTrades.remove(trade);
|
|
||||||
closedTradableManager.add(trade);
|
|
||||||
|
|
||||||
resultHandler.handleResult();
|
resultHandler.handleResult();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -357,7 +350,7 @@ public class TradeManager {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
try {
|
try {
|
||||||
walletService.sendFunds(fromAddress, toAddress, trade.getPayoutAmount(), callback);
|
walletService.sendFunds(fromAddress, toAddress, trade.getPayoutAmount(), aesKey, callback);
|
||||||
} catch (AddressFormatException | InsufficientMoneyException e) {
|
} catch (AddressFormatException | InsufficientMoneyException e) {
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
log.error(e.getMessage());
|
log.error(e.getMessage());
|
||||||
@ -365,26 +358,57 @@ public class TradeManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// In a fault case we remove it and add it to the closed trades
|
// If trade was completed (closed without fault but might be closed by a dispute) we move it to the closed trades
|
||||||
public void addTradeToClosedTrades(Trade trade) {
|
private void addTradeToClosedTrades(Trade trade) {
|
||||||
pendingTrades.remove(trade);
|
trades.remove(trade);
|
||||||
closedTradableManager.add(trade);
|
closedTradableManager.add(trade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If trade is in already in critical state (if taker role: taker fee; both roles: after deposit published)
|
||||||
|
// we move the trade to failedTradesManager
|
||||||
public void addTradeToFailedTrades(Trade trade) {
|
public void addTradeToFailedTrades(Trade trade) {
|
||||||
pendingTrades.remove(trade);
|
trades.remove(trade);
|
||||||
failedTradesManager.add(trade);
|
failedTradesManager.add(trade);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If trade is in preparation (if taker role: before taker fee is paid; both roles: before deposit published)
|
||||||
|
// we just remove the trade from our list. We don't store those trades.
|
||||||
|
public void removePreparedTrade(Trade trade) {
|
||||||
|
trades.remove(trade);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// Dispute
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
public void closeDisputedTrade(String tradeId) {
|
||||||
|
Optional<Trade> tradeOptional = getTradeById(tradeId);
|
||||||
|
if (tradeOptional.isPresent()) {
|
||||||
|
Trade trade = tradeOptional.get();
|
||||||
|
trade.setDisputeState(Trade.DisputeState.DISPUTE_CLOSED);
|
||||||
|
addTradeToClosedTrades(trade);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
// Getters
|
// Getters
|
||||||
///////////////////////////////////////////////////////////////////////////////////////////
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
public ObservableList<Trade> getPendingTrades() {
|
public ObservableList<Trade> getTrades() {
|
||||||
return pendingTrades.getObservableList();
|
return trades.getObservableList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BooleanProperty pendingTradesInitializedProperty() {
|
||||||
|
return pendingTradesInitialized;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMyOffer(Offer offer) {
|
public boolean isMyOffer(Offer offer) {
|
||||||
return offer.isMyOffer(keyRing);
|
return offer.isMyOffer(keyRing);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Optional<Trade> getTradeById(String tradeId) {
|
||||||
|
return trades.stream().filter(e -> e.getId().equals(tradeId)).findFirst();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -17,18 +17,15 @@
|
|||||||
|
|
||||||
package io.bitsquare.trade;
|
package io.bitsquare.trade;
|
||||||
|
|
||||||
import io.bitsquare.BitsquareModule;
|
import com.google.inject.Singleton;
|
||||||
|
import io.bitsquare.app.AppModule;
|
||||||
import io.bitsquare.trade.closed.ClosedTradableManager;
|
import io.bitsquare.trade.closed.ClosedTradableManager;
|
||||||
import io.bitsquare.trade.failed.FailedTradesManager;
|
import io.bitsquare.trade.failed.FailedTradesManager;
|
||||||
|
|
||||||
import com.google.inject.Singleton;
|
|
||||||
|
|
||||||
import org.slf4j.Logger;
|
import org.slf4j.Logger;
|
||||||
import org.slf4j.LoggerFactory;
|
import org.slf4j.LoggerFactory;
|
||||||
|
|
||||||
import org.springframework.core.env.Environment;
|
import org.springframework.core.env.Environment;
|
||||||
|
|
||||||
public class TradeModule extends BitsquareModule {
|
public class TradeModule extends AppModule {
|
||||||
private static final Logger log = LoggerFactory.getLogger(TradeModule.class);
|
private static final Logger log = LoggerFactory.getLogger(TradeModule.class);
|
||||||
|
|
||||||
public TradeModule(Environment env) {
|
public TradeModule(Environment env) {
|
||||||
|
@ -1,111 +0,0 @@
|
|||||||
/*
|
|
||||||
* This file is part of Bitsquare.
|
|
||||||
*
|
|
||||||
* Bitsquare is free software: you can redistribute it and/or modify it
|
|
||||||
* under the terms of the GNU Affero General Public License as published by
|
|
||||||
* the Free Software Foundation, either version 3 of the License, or (at
|
|
||||||
* your option) any later version.
|
|
||||||
*
|
|
||||||
* Bitsquare is distributed in the hope that it will be useful, but WITHOUT
|
|
||||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
||||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public
|
|
||||||
* License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Affero General Public License
|
|
||||||
* along with Bitsquare. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package io.bitsquare.trade;
|
|
||||||
|
|
||||||
public interface TradeState {
|
|
||||||
Phase getPhase();
|
|
||||||
|
|
||||||
void setPhase(Phase phase);
|
|
||||||
|
|
||||||
enum SellerState implements TradeState {
|
|
||||||
PREPARATION(Phase.PREPARATION),
|
|
||||||
|
|
||||||
DEPOSIT_PUBLISHED_MSG_RECEIVED(Phase.DEPOSIT_PAID),
|
|
||||||
DEPOSIT_CONFIRMED(Phase.DEPOSIT_PAID),
|
|
||||||
|
|
||||||
FIAT_PAYMENT_STARTED_MSG_RECEIVED(Phase.FIAT_SENT),
|
|
||||||
|
|
||||||
FIAT_PAYMENT_RECEIPT(Phase.FIAT_RECEIVED),
|
|
||||||
FIAT_PAYMENT_RECEIPT_MSG_SENT(Phase.FIAT_RECEIVED),
|
|
||||||
|
|
||||||
PAYOUT_TX_RECEIVED(Phase.PAYOUT_PAID),
|
|
||||||
PAYOUT_TX_COMMITTED(Phase.PAYOUT_PAID),
|
|
||||||
|
|
||||||
PAYOUT_BROAD_CASTED(Phase.PAYOUT_PAID),
|
|
||||||
|
|
||||||
WITHDRAW_COMPLETED(Phase.WITHDRAWN),
|
|
||||||
|
|
||||||
FAILED();
|
|
||||||
|
|
||||||
public Phase getPhase() {
|
|
||||||
return phase;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPhase(Phase phase) {
|
|
||||||
this.phase = phase;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Phase phase;
|
|
||||||
|
|
||||||
SellerState() {
|
|
||||||
}
|
|
||||||
|
|
||||||
SellerState(Phase phase) {
|
|
||||||
this.phase = phase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum BuyerState implements TradeState {
|
|
||||||
PREPARATION(Phase.PREPARATION),
|
|
||||||
|
|
||||||
DEPOSIT_PUBLISHED(Phase.DEPOSIT_PAID),
|
|
||||||
DEPOSIT_PUBLISHED_MSG_SENT(Phase.DEPOSIT_PAID),
|
|
||||||
DEPOSIT_CONFIRMED(Phase.DEPOSIT_PAID),
|
|
||||||
|
|
||||||
FIAT_PAYMENT_STARTED(Phase.FIAT_SENT),
|
|
||||||
FIAT_PAYMENT_STARTED_MSG_SENT(Phase.FIAT_SENT),
|
|
||||||
|
|
||||||
FIAT_PAYMENT_RECEIPT_MSG_RECEIVED(Phase.FIAT_RECEIVED),
|
|
||||||
|
|
||||||
PAYOUT_TX_COMMITTED(Phase.PAYOUT_PAID),
|
|
||||||
PAYOUT_TX_SENT(Phase.PAYOUT_PAID),
|
|
||||||
|
|
||||||
PAYOUT_BROAD_CASTED(Phase.PAYOUT_PAID),
|
|
||||||
|
|
||||||
WITHDRAW_COMPLETED(Phase.WITHDRAWN),
|
|
||||||
|
|
||||||
FAILED();
|
|
||||||
|
|
||||||
public Phase getPhase() {
|
|
||||||
return phase;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setPhase(Phase phase) {
|
|
||||||
this.phase = phase;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Phase phase;
|
|
||||||
|
|
||||||
BuyerState() {
|
|
||||||
}
|
|
||||||
|
|
||||||
BuyerState(Phase phase) {
|
|
||||||
this.phase = phase;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
enum Phase {
|
|
||||||
PREPARATION,
|
|
||||||
TAKER_FEE_PAID,
|
|
||||||
DEPOSIT_PAID,
|
|
||||||
FIAT_SENT,
|
|
||||||
FIAT_RECEIVED,
|
|
||||||
PAYOUT_PAID,
|
|
||||||
WITHDRAWN
|
|
||||||
}
|
|
||||||
}
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user