package io.bitsquare.p2p;
import ch.qos.logback.classic.Level;
import com.google.common.annotations.VisibleForTesting;
import io.bitsquare.app.Log;
import io.bitsquare.app.Version;
import io.bitsquare.common.Clock;
import io.bitsquare.common.CommonOptionKeys;
import io.bitsquare.common.UserThread;
import io.bitsquare.common.util.Utilities;
import io.bitsquare.network.NetworkOptionKeys;
import io.bitsquare.p2p.peers.BanList;
import io.bitsquare.p2p.seed.SeedNodesRepository;
import org.jetbrains.annotations.Nullable;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.File;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import static com.google.common.base.Preconditions.checkArgument;
// Previously used seednode class, replaced now by bootstrap module. We keep it here as it was used in tests...
public class DummySeedNode {
private static final Logger log = LoggerFactory.getLogger(DummySeedNode.class);
public static final int MAX_CONNECTIONS_LIMIT = 1000;
public static final int MAX_CONNECTIONS_DEFAULT = 50;
public static final String SEED_NODES_LIST = "seedNodes";
public static final String HELP = "help";
private NodeAddress mySeedNodeAddress = new NodeAddress("localhost:8001");
private int maxConnections = MAX_CONNECTIONS_DEFAULT; // we keep default a higher connection size for seed nodes
private boolean useLocalhost = false;
private Set<NodeAddress> progArgSeedNodes;
private P2PService seedNodeP2PService;
private boolean stopped;
private final String defaultUserDataDir;
private Level logLevel = Level.WARN;
public DummySeedNode(String defaultUserDataDir) {
Log.traceCall("defaultUserDataDir=" + defaultUserDataDir);
this.defaultUserDataDir = defaultUserDataDir;
}
///////////////////////////////////////////////////////////////////////////////////////////
// API
///////////////////////////////////////////////////////////////////////////////////////////
// args: myAddress (incl. port) bitcoinNetworkId maxConnections useLocalhost seedNodes (separated with |)
// 2. and 3. args are optional
// eg. lmvdenjkyvx2ovga.onion:8001 0 20 false eo5ay2lyzrfvx2nr.onion:8002|si3uu56adkyqkldl.onion:8003
// or when using localhost: localhost:8001 2 20 true localhost:8002|localhost:8003
// BitcoinNetworkId: The id for the bitcoin network (Mainnet = 0, TestNet = 1, Regtest = 2)
// localhost:3002 2 50 true
// localhost:3002 2 50 localhost:4442|localhost:4443 true
// Usage: -myAddress=<my onion address> -networkId=<networkId (Mainnet = 0, TestNet = 1, Regtest = 2)> -maxConnections=<No. of max. connections allowed> -useLocalhost=false -seedNodes=si3uu56adkyqkldl.onion:8002|eo5ay2lyzrfvx2nr.onion:8002 -ignore=4543y2lyzrfvx2nr.onion:8002|876572lyzrfvx2nr.onion:8002
// Example usage: -myAddress=lmvdenjkyvx2ovga.onion:8001 -networkId=0 -maxConnections=20 -useLocalhost=false -seedNodes=si3uu56adkyqkldl.onion:8002|eo5ay2lyzrfvx2nr.onion:8002 -ignore=4543y2lyzrfvx2nr.onion:8002|876572lyzrfvx2nr.onion:8002
public static final String USAGE = "Usage:\n" +
"--myAddress=<my onion address>\n" +
"--networkId=[0|1|2] (Mainnet = 0, TestNet = 1, Regtest = 2)\n" +
"--maxConnections=<No. of max. connections allowed>\n" +
"--useLocalhost=[true|false]\n" +
"--logLevel=Log level [OFF, ALL, ERROR, WARN, INFO, DEBUG, TRACE]\n" +
"--seedNodes=[onion addresses separated with comma]\n" +
"--banList=[onion addresses separated with comma]\n" +
"--help";
public void processArgs(String[] args) {
int networkId = -1;
try {
for (int i = 0; i < args.length; i++) {
String arg = args[i];
if (arg.startsWith("--"))
arg = arg.substring(2);
if (arg.startsWith(NetworkOptionKeys.MY_ADDRESS)) {
arg = arg.substring(NetworkOptionKeys.MY_ADDRESS.length() + 1);
checkArgument(arg.contains(":") && arg.split(":").length == 2 && arg.split(":")[1].length() > 3, "Wrong program argument: " + arg);
mySeedNodeAddress = new NodeAddress(arg);
log.debug("From processArgs: mySeedNodeAddress=" + mySeedNodeAddress);
} else if (arg.startsWith(NetworkOptionKeys.NETWORK_ID)) {
arg = arg.substring(NetworkOptionKeys.NETWORK_ID.length() + 1);
networkId = Integer.parseInt(arg);
log.debug("From processArgs: networkId=" + networkId);
checkArgument(networkId > -1 && networkId < 3,
"networkId out of scope (Mainnet = 0, TestNet = 1, Regtest = 2)");
Version.setBtcNetworkId(networkId);
} else if (arg.startsWith(NetworkOptionKeys.MAX_CONNECTIONS)) {
arg = arg.substring(NetworkOptionKeys.MAX_CONNECTIONS.length() + 1);
maxConnections = Integer.parseInt(arg);
log.debug("From processArgs: maxConnections=" + maxConnections);
checkArgument(maxConnections < MAX_CONNECTIONS_LIMIT, "maxConnections seems to be a bit too high...");
} else if (arg.startsWith(NetworkOptionKeys.USE_LOCALHOST)) {
arg = arg.substring(NetworkOptionKeys.USE_LOCALHOST.length() + 1);
checkArgument(arg.equals("true") || arg.equals("false"));
useLocalhost = ("true").equals(arg);
log.debug("From processArgs: useLocalhost=" + useLocalhost);
} else if (arg.startsWith(CommonOptionKeys.LOG_LEVEL_KEY)) {
arg = arg.substring(CommonOptionKeys.LOG_LEVEL_KEY.length() + 1);
logLevel = Level.toLevel(arg.toUpperCase());
log.debug("From processArgs: logLevel=" + logLevel);
} else if (arg.startsWith(SEED_NODES_LIST)) {
arg = arg.substring(SEED_NODES_LIST.length() + 1);
checkArgument(arg.contains(":") && arg.split(":").length > 1 && arg.split(":")[1].length() > 3,
"Wrong program argument " + arg);
List<String> list = Arrays.asList(arg.split(","));
progArgSeedNodes = new HashSet<>();
list.forEach(e -> {
checkArgument(e.contains(":") && e.split(":").length == 2 && e.split(":")[1].length() == 4,
"Wrong program argument " + e);
progArgSeedNodes.add(new NodeAddress(e));
});
log.debug("From processArgs: progArgSeedNodes=" + progArgSeedNodes);
progArgSeedNodes.remove(mySeedNodeAddress);
} else if (arg.startsWith(NetworkOptionKeys.BAN_LIST)) {
arg = arg.substring(NetworkOptionKeys.BAN_LIST.length() + 1);
checkArgument(arg.contains(":") && arg.split(":").length > 1 && arg.split(":")[1].length() > 3,
"Wrong program argument " + arg);
List<String> list = Arrays.asList(arg.split(","));
list.forEach(e -> {
checkArgument(e.contains(":") && e.split(":").length == 2 && e.split(":")[1].length() == 4,
"Wrong program argument " + e);
BanList.add(new NodeAddress(e));
});
log.debug("From processArgs: ignoreList=" + list);
} else if (arg.startsWith(HELP)) {
log.debug(USAGE);
} else {
log.error("Invalid argument. " + arg + "\n" + USAGE);
}
}
if (mySeedNodeAddress == null)
log.error("My seed node must be set.\n" + USAGE);
if (networkId == -1)
log.error("NetworkId must be set.\n" + USAGE);
} catch (Throwable t) {
log.error("Some arguments caused an exception. " + Arrays.toString(args) + "\nException: " + t.getMessage() + "\n" + USAGE);
shutDown();
}
}
public void createAndStartP2PService(boolean useDetailedLogging) {
createAndStartP2PService(mySeedNodeAddress, maxConnections, useLocalhost,
Version.getBtcNetworkId(), useDetailedLogging, progArgSeedNodes, null);
}
@VisibleForTesting
public void createAndStartP2PService(NodeAddress mySeedNodeAddress,
int maxConnections,
boolean useLocalhost,
int networkId,
boolean useDetailedLogging,
@Nullable Set<NodeAddress> progArgSeedNodes,
@Nullable P2PServiceListener listener) {
Path appPath = Paths.get(defaultUserDataDir,
"Bitsquare_seed_node_" + String.valueOf(mySeedNodeAddress.getFullAddress().replace(":", "_")));
String logPath = Paths.get(appPath.toString(), "logs").toString();
Log.setup(logPath);
log.debug("Log files under: " + logPath);
Version.printVersion();
Utilities.printSysInfo();
Log.setLevel(logLevel);
SeedNodesRepository seedNodesRepository = new SeedNodesRepository();
if (progArgSeedNodes != null && !progArgSeedNodes.isEmpty()) {
if (useLocalhost)
seedNodesRepository.setLocalhostSeedNodeAddresses(progArgSeedNodes);
else
seedNodesRepository.setTorSeedNodeAddresses(progArgSeedNodes);
}
File storageDir = Paths.get(appPath.toString(), "db").toFile();
if (storageDir.mkdirs())
log.debug("Created storageDir at " + storageDir.getAbsolutePath());
File torDir = Paths.get(appPath.toString(), "tor").toFile();
if (torDir.mkdirs())
log.debug("Created torDir at " + torDir.getAbsolutePath());
seedNodesRepository.setNodeAddressToExclude(mySeedNodeAddress);
seedNodeP2PService = new P2PService(seedNodesRepository, mySeedNodeAddress.port, maxConnections,
torDir, useLocalhost, networkId, storageDir, null, null, null, new Clock(), null, null, null);
seedNodeP2PService.start(listener);
}
@VisibleForTesting
public P2PService getSeedNodeP2PService() {
return seedNodeP2PService;
}
private void shutDown() {
shutDown(null);
}
public void shutDown(@Nullable Runnable shutDownCompleteHandler) {
if (!stopped) {
stopped = true;
seedNodeP2PService.shutDown(() -> {
if (shutDownCompleteHandler != null) UserThread.execute(shutDownCompleteHandler);
});
}
}
}