/* This code is part of Freenet. It is distributed under the GNU General * Public License, version 2 (or at your option any later version). See * http://www.gnu.org/ for further details of the GPL. */ package freenet.node.simulator; import java.io.File; import freenet.crypt.DummyRandomSource; import freenet.crypt.RandomSource; import freenet.node.LocationManager; import freenet.node.Node; import freenet.node.NodeStarter; import freenet.support.Executor; import freenet.support.Logger; import freenet.support.PooledExecutor; import freenet.support.Logger.LogLevel; import freenet.support.io.FileUtil; import freenet.support.math.BootstrappingDecayingRunningAverage; import freenet.support.math.RunningAverage; import freenet.support.math.SimpleRunningAverage; /** * @author amphibian * * Create a mesh of nodes and let them sort out their locations. * * Then run some node-to-node searches. */ public class RealNodeRoutingTest extends RealNodeTest { static final int NUMBER_OF_NODES = 100; static final int DEGREE = 10; static final short MAX_HTL = (short) 5; static final boolean START_WITH_IDEAL_LOCATIONS = true; static final boolean FORCE_NEIGHBOUR_CONNECTIONS = true; static final int MAX_PINGS = 2000; static final boolean ENABLE_SWAPPING = false; static final boolean ENABLE_SWAP_QUEUEING = false; static final boolean ENABLE_FOAF = true; public static int DARKNET_PORT_BASE = RealNodeRequestInsertTest.DARKNET_PORT_END; public static final int DARKNET_PORT_END = DARKNET_PORT_BASE + NUMBER_OF_NODES; public static void main(String[] args) throws Exception { System.out.println("Routing test using real nodes:"); System.out.println(); String dir = "realNodeRequestInsertTest"; File wd = new File(dir); if(!FileUtil.removeAll(wd)) { System.err.println("Mass delete failed, test may not be accurate."); System.exit(EXIT_CANNOT_DELETE_OLD_DATA); } wd.mkdir(); //NOTE: globalTestInit returns in ignored random source NodeStarter.globalTestInit(dir, false, LogLevel.ERROR, "", true); // Make the network reproducible so we can easily compare different routing options by specifying a seed. DummyRandomSource random = new DummyRandomSource(3142); //DiffieHellman.init(random); Node[] nodes = new Node[NUMBER_OF_NODES]; Logger.normal(RealNodeRoutingTest.class, "Creating nodes..."); Executor executor = new PooledExecutor(); for(int i = 0; i < NUMBER_OF_NODES; i++) { System.err.println("Creating node " + i); nodes[i] = NodeStarter.createTestNode(DARKNET_PORT_BASE + i, 0, dir, true, MAX_HTL, 0 /* no dropped packets */, random, executor, 500 * NUMBER_OF_NODES, 65536, true, ENABLE_SWAPPING, false, false, false, ENABLE_SWAP_QUEUEING, true, 0, ENABLE_FOAF, false, true, false, null); Logger.normal(RealNodeRoutingTest.class, "Created node " + i); } Logger.normal(RealNodeRoutingTest.class, "Created " + NUMBER_OF_NODES + " nodes"); // Now link them up makeKleinbergNetwork(nodes, START_WITH_IDEAL_LOCATIONS, DEGREE, FORCE_NEIGHBOUR_CONNECTIONS, random); Logger.normal(RealNodeRoutingTest.class, "Added random links"); for(int i = 0; i < NUMBER_OF_NODES; i++) { System.err.println("Starting node " + i); nodes[i].start(false); } waitForAllConnected(nodes); // Make the choice of nodes to ping to and from deterministic too. // There is timing noise because of all the nodes, but the network // and the choice of nodes to start and finish are deterministic, so // the overall result should be more or less deterministic. waitForPingAverage(0.98, nodes, new DummyRandomSource(3143), MAX_PINGS, 5000); System.exit(0); } static void waitForPingAverage(double accuracy, Node[] nodes, RandomSource random, int maxTests, int sleepTime) throws InterruptedException { int totalHopsTaken = 0; int cycleNumber = 0; int lastSwaps = 0; int lastNoSwaps = 0; int failures = 0; int successes = 0; RunningAverage avg = new SimpleRunningAverage(100, 0.0); RunningAverage avg2 = new BootstrappingDecayingRunningAverage(0.0, 0.0, 1.0, 100, null); int pings = 0; for(int total = 0; total < maxTests; total++) { cycleNumber++; try { Thread.sleep(sleepTime); } catch(InterruptedException e) { // Ignore } for(int i = 0; i < nodes.length; i++) { System.err.println("Cycle " + cycleNumber + " node " + i + ": " + nodes[i].getLocation()); } int newSwaps = LocationManager.swaps; int totalStarted = LocationManager.startedSwaps; int noSwaps = LocationManager.noSwaps; System.err.println("Swaps: " + (newSwaps - lastSwaps)); System.err.println("\nTotal swaps: Started*2: " + totalStarted * 2 + ", succeeded: " + newSwaps + ", last minute failures: " + noSwaps + ", ratio " + (double) noSwaps / (double) newSwaps + ", early failures: " + ((totalStarted * 2) - (noSwaps + newSwaps))); System.err.println("This cycle ratio: " + ((double) (noSwaps - lastNoSwaps)) / ((double) (newSwaps - lastSwaps))); lastNoSwaps = noSwaps; System.err.println("Swaps rejected (already locked): " + LocationManager.swapsRejectedAlreadyLocked); System.err.println("Swaps rejected (nowhere to go): " + LocationManager.swapsRejectedNowhereToGo); System.err.println("Swaps rejected (rate limit): " + LocationManager.swapsRejectedRateLimit); System.err.println("Swaps rejected (recognized ID):" + LocationManager.swapsRejectedRecognizedID); System.err.println("Swaps failed:" + LocationManager.noSwaps); System.err.println("Swaps succeeded:" + LocationManager.swaps); double totalSwapInterval = 0.0; double totalSwapTime = 0.0; for(int i = 0; i < nodes.length; i++) { totalSwapInterval += nodes[i].lm.getSendSwapInterval(); totalSwapTime += nodes[i].lm.getAverageSwapTime(); } System.err.println("Average swap time: " + (totalSwapTime / nodes.length)); System.err.println("Average swap sender interval: " + (totalSwapInterval / nodes.length)); waitForAllConnected(nodes); lastSwaps = newSwaps; // Do some (routed) test-pings for(int i = 0; i < 10; i++) { try { Thread.sleep(sleepTime); } catch(InterruptedException e1) { } try { Node randomNode = nodes[random.nextInt(nodes.length)]; Node randomNode2 = randomNode; while(randomNode2 == randomNode) { randomNode2 = nodes[random.nextInt(nodes.length)]; } double loc2 = randomNode2.getLocation(); Logger.normal(RealNodeRoutingTest.class, "Pinging " + randomNode2.getDarknetPortNumber() + " @ " + loc2 + " from " + randomNode.getDarknetPortNumber() + " @ " + randomNode.getLocation()); int hopsTaken = randomNode.routedPing(loc2, randomNode2.getDarknetPubKeyHash()); pings++; if(hopsTaken < 0) { failures++; avg.report(0.0); avg2.report(0.0); double ratio = (double) successes / ((double) (failures + successes)); System.err.println("Routed ping " + pings + " FAILED from " + randomNode.getDarknetPortNumber() + " to " + randomNode2.getDarknetPortNumber() + " (long:" + ratio + ", short:" + avg.currentValue() + ", vague:" + avg2.currentValue() + ')'); } else { totalHopsTaken += hopsTaken; successes++; avg.report(1.0); avg2.report(1.0); double ratio = (double) successes / ((double) (failures + successes)); System.err.println("Routed ping " + pings + " success: " + hopsTaken + ' ' + randomNode.getDarknetPortNumber() + " to " + randomNode2.getDarknetPortNumber() + " (long:" + ratio + ", short:" + avg.currentValue() + ", vague:" + avg2.currentValue() + ')'); } } catch(Throwable t) { Logger.error(RealNodeRoutingTest.class, "Caught " + t, t); } } System.err.println("Average path length for successful requests: "+totalHopsTaken/successes); if(pings > 10 && avg.currentValue() > accuracy && ((double) successes / ((double) (failures + successes)) > accuracy)) { System.err.println(); System.err.println("Reached " + (accuracy * 100) + "% accuracy."); System.err.println(); System.err.println("Network size: " + nodes.length); System.err.println("Maximum HTL: " + MAX_HTL); System.err.println("Average path length for successful requests: "+totalHopsTaken/successes); System.err.println("Total started swaps: " + LocationManager.startedSwaps); System.err.println("Total rejected swaps (already locked): " + LocationManager.swapsRejectedAlreadyLocked); System.err.println("Total swaps rejected (nowhere to go): " + LocationManager.swapsRejectedNowhereToGo); System.err.println("Total swaps rejected (rate limit): " + LocationManager.swapsRejectedRateLimit); System.err.println("Total swaps rejected (recognized ID):" + LocationManager.swapsRejectedRecognizedID); System.err.println("Total swaps failed:" + LocationManager.noSwaps); System.err.println("Total swaps succeeded:" + LocationManager.swaps); return; } } System.exit(EXIT_PING_TARGET_NOT_REACHED); } }