/*
* Copyright (c) [2016] [ <ether.camp> ]
* This file is part of the ethereumJ library.
*
* The ethereumJ library is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* The ethereumJ library 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 Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with the ethereumJ library. If not, see <http://www.gnu.org/licenses/>.
*/
package org.ethereum.samples;
import com.google.common.base.Joiner;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigValue;
import com.typesafe.config.ConfigValueFactory;
import org.ethereum.config.SystemProperties;
import org.ethereum.crypto.ECKey;
import org.ethereum.facade.EthereumFactory;
import org.ethereum.net.rlpx.discover.NodeManager;
import org.ethereum.net.rlpx.discover.table.NodeEntry;
import org.ethereum.net.server.Channel;
import org.ethereum.net.server.ChannelManager;
import org.spongycastle.util.encoders.Hex;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
/**
* Testing peers discovery.
*
* The sample creates a small private net with three peers:
* - first is point for discovery;
* - two other ones will connect to first.
*
* Peers run on same IP 127.0.0.1 and are different by ports.
*
* After some time all peers should find each other. We use `peer.discovery.ip.list` config
* option to point to discovery peer.
*
* Created by Stan Reshetnyk on 06.10.2016.
*/
public class PrivateNetworkDiscoverySample {
/**
* Creating 3 instances with different config classes
*/
public static void main(String[] args) throws Exception {
BasicSample.sLogger.info("Starting main node to which others will connect to");
EthereumFactory.createEthereum(Node0Config.class);
BasicSample.sLogger.info("Starting regular instance 1!");
EthereumFactory.createEthereum(Node1Config.class);
BasicSample.sLogger.info("Starting regular instance 2!");
EthereumFactory.createEthereum(Node2Config.class);
}
/**
* Spring configuration class for the Regular peer
*/
private static class RegularConfig {
private final String discoveryNode;
private final int nodeIndex;
public RegularConfig(int nodeIndex, String discoveryNode) {
this.nodeIndex = nodeIndex;
this.discoveryNode = discoveryNode;
}
@Bean
public BasicSample node() {
return new BasicSample("sampleNode-" + nodeIndex) {
@Autowired
ChannelManager channelManager;
@Autowired
NodeManager nodeManager;
{
new Thread(new Runnable() {
@Override
public void run() {
try {
Thread.sleep(5000);
while (true) {
if (logger != null) {
Thread.sleep(15000);
if (channelManager != null) {
final Collection<Channel> activePeers = channelManager.getActivePeers();
final ArrayList<String> ports = new ArrayList<>();
for (Channel channel: activePeers) {
ports.add(channel.getInetSocketAddress().getHostName() + ":" + channel.getInetSocketAddress().getPort());
}
final Collection<NodeEntry> nodes = nodeManager.getTable().getAllNodes();
final ArrayList<String> nodesString = new ArrayList<>();
for (NodeEntry node: nodes) {
nodesString.add(node.getNode().getHost() + ":" + node.getNode().getPort() + "@" + node.getNode().getHexId().substring(0, 6) );
}
logger.info("channelManager.getActivePeers() " + activePeers.size() + " " + Joiner.on(", ").join(ports));
logger.info("nodeManager.getTable().getAllNodes() " + nodesString.size() + " " + Joiner.on(", ").join(nodesString));
} else {
logger.info("Channel manager is null");
}
} else {
System.err.println("Logger is null for " + nodeIndex);
}
}
} catch (Exception e) {
logger.error("Error checking peers count: ", e);
}
}
}).start();
}
@Override
public void onSyncDone() {
logger.info("onSyncDone");
}
};
}
/**
* Instead of supplying properties via config file for the peer
* we are substituting the corresponding bean which returns required
* config for this instance.
*/
@Bean
public SystemProperties systemProperties() {
return new SystemProperties(getConfig(nodeIndex, discoveryNode));
}
}
public static class Node0Config extends RegularConfig {
public Node0Config() {
super(0, null);
}
@Bean
public SystemProperties systemProperties() {
return super.systemProperties();
}
@Bean
public BasicSample node() {
return super.node();
}
}
private static class Node1Config extends RegularConfig {
public Node1Config() {
super(1, "127.0.0.1:20000");
}
@Bean
public SystemProperties systemProperties() {
return super.systemProperties();
}
@Bean
public BasicSample node() {
return super.node();
}
}
private static class Node2Config extends RegularConfig{
public Node2Config() {
super(2, "127.0.0.1:20000");
}
@Bean
public SystemProperties systemProperties() {
return super.systemProperties();
}
@Bean
public BasicSample node() {
return super.node();
}
}
private static Config getConfig(int index, String discoveryNode) {
return ConfigFactory.empty()
.withValue("peer.discovery.enabled", value(true))
.withValue("peer.discovery.external.ip", value("127.0.0.1"))
.withValue("peer.discovery.bind.ip", value("127.0.0.1"))
.withValue("peer.discovery.persist", value("false"))
.withValue("peer.listen.port", value(20000 + index))
.withValue("peer.privateKey", value(Hex.toHexString(ECKey.fromPrivate(("" + index).getBytes()).getPrivKeyBytes())))
.withValue("peer.networkId", value(555))
.withValue("sync.enabled", value(true))
.withValue("database.incompatibleDatabaseBehavior", value("RESET"))
.withValue("genesis", value("sample-genesis.json"))
.withValue("database.dir", value("sampleDB-" + index))
.withValue("peer.discovery.ip.list", value(discoveryNode != null ? Arrays.asList(discoveryNode) : Arrays.asList()));
}
private static ConfigValue value(Object value) {
return ConfigValueFactory.fromAnyRef(value);
}
}