/* 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 freenet.crypt.RandomSource; import freenet.io.comm.PeerParseException; import freenet.node.FSParseException; import freenet.node.Location; import freenet.node.Node; import freenet.node.NodeInitException; import freenet.node.NodeStats; import freenet.node.PeerNode; import freenet.node.DarknetPeerNode.FRIEND_TRUST; import freenet.node.DarknetPeerNode.FRIEND_VISIBILITY; import freenet.node.PeerTooOldException; import freenet.support.LogThresholdCallback; import freenet.support.Logger; import freenet.support.Logger.LogLevel; /** * Optional base class for RealNode*Test. * Has some useful utilities. * @author toad * @author robert */ public class RealNodeTest { static final int EXIT_BASE = NodeInitException.EXIT_NODE_UPPER_LIMIT; static final int EXIT_CANNOT_DELETE_OLD_DATA = EXIT_BASE + 3; static final int EXIT_PING_TARGET_NOT_REACHED = EXIT_BASE + 4; static final int EXIT_INSERT_FAILED = EXIT_BASE + 5; static final int EXIT_REQUEST_FAILED = EXIT_BASE + 6; static final int EXIT_BAD_DATA = EXIT_BASE + 7; static final FRIEND_TRUST trust = FRIEND_TRUST.LOW; static final FRIEND_VISIBILITY visibility = FRIEND_VISIBILITY.NO; private static volatile boolean logMINOR; static { Logger.registerLogThresholdCallback(new LogThresholdCallback(){ @Override public void shouldUpdate(){ logMINOR = Logger.shouldLog(LogLevel.MINOR, this); } }); } /* Because we start a whole bunch of nodes at once, we will get many "Not reusing * tracker, so wiping old trackers" messages. This is normal, all the nodes start * handshaking straight off, they all send JFK(1)s, and we get race conditions. */ /* Borrowed from mrogers simulation code (February 6, 2008) -- FIXME: May not generate good networks. Presumably this is because the arrays are always scanned [0..n], some nodes tend to have *much* higher connections than the degree (the first few), starving the latter ones. */ static void makeKleinbergNetwork (Node[] nodes, boolean idealLocations, int degree, boolean forceNeighbourConnections, RandomSource random) { if(idealLocations) { // First set the locations up so we don't spend a long time swapping just to stabilise each network. double div = 1.0 / nodes.length; double loc = 0.0; for (int i=0; i<nodes.length; i++) { nodes[i].setLocation(loc); loc += div; } } if(forceNeighbourConnections) { for(int i=0;i<nodes.length;i++) { int next = (i+1) % nodes.length; connect(nodes[i], nodes[next]); } } for (int i=0; i<nodes.length; i++) { Node a = nodes[i]; // Normalise the probabilities double norm = 0.0; for (int j=0; j<nodes.length; j++) { Node b = nodes[j]; if (a.getLocation() == b.getLocation()) continue; norm += 1.0 / distance (a, b); } // Create degree/2 outgoing connections for (int k=0; k<nodes.length; k++) { Node b = nodes[k]; if (a.getLocation() == b.getLocation()) continue; double p = 1.0 / distance (a, b) / norm; for (int n = 0; n < degree / 2; n++) { if (random.nextFloat() < p) { connect(a, b); break; } } } } } static void connect(Node a, Node b) { try { a.connect (b, trust, visibility); b.connect (a, trust, visibility); } catch (FSParseException e) { Logger.error(RealNodeTest.class, "cannot connect!!!!", e); } catch (PeerParseException e) { Logger.error(RealNodeTest.class, "cannot connect #2!!!!", e); } catch (freenet.io.comm.ReferenceSignatureVerificationException e) { Logger.error(RealNodeTest.class, "cannot connect #3!!!!", e); } catch (PeerTooOldException e) { Logger.error(RealNodeTest.class, "cannot connect #4!!!!", e); } } static double distance(Node a, Node b) { double aL=a.getLocation(); double bL=b.getLocation(); return Location.distance(aL, bL); } static String getPortNumber(PeerNode p) { if (p == null || p.getPeer() == null) return "null"; return Integer.toString(p.getPeer().getPort()); } static String getPortNumber(Node n) { if (n == null) return "null"; return Integer.toString(n.getDarknetPortNumber()); } static void waitForAllConnected(Node[] nodes) throws InterruptedException { long tStart = System.currentTimeMillis(); while(true) { int countFullyConnected = 0; int countReallyConnected = 0; int totalPeers = 0; int totalConnections = 0; int totalPartialConnections = 0; int totalCompatibleConnections = 0; int totalBackedOff = 0; double totalPingTime = 0.0; double maxPingTime = 0.0; double minPingTime = Double.MAX_VALUE; for(int i=0;i<nodes.length;i++) { int countConnected = nodes[i].peers.countConnectedDarknetPeers(); int countAlmostConnected = nodes[i].peers.countAlmostConnectedDarknetPeers(); int countTotal = nodes[i].peers.countValidPeers(); int countBackedOff = nodes[i].peers.countBackedOffPeers(false); int countCompatible = nodes[i].peers.countCompatibleDarknetPeers(); totalPeers += countTotal; totalConnections += countConnected; totalPartialConnections += countAlmostConnected; totalCompatibleConnections += countCompatible; totalBackedOff += countBackedOff; double pingTime = nodes[i].nodeStats.getNodeAveragePingTime(); totalPingTime += pingTime; if(pingTime > maxPingTime) maxPingTime = pingTime; if(pingTime < minPingTime) minPingTime = pingTime; if(countConnected == countTotal) { countFullyConnected++; if(countBackedOff == 0) countReallyConnected++; } else { if(logMINOR) Logger.minor(RealNodeTest.class, "Connection count for "+nodes[i]+" : "+countConnected+" partial "+countAlmostConnected); } if(countBackedOff > 0) { if(logMINOR) Logger.minor(RealNodeTest.class, "Backed off: "+nodes[i]+" : "+countBackedOff); } } double avgPingTime = totalPingTime / nodes.length; if(countFullyConnected == nodes.length && countReallyConnected == nodes.length && totalBackedOff == 0 && minPingTime < NodeStats.DEFAULT_SUB_MAX_PING_TIME && maxPingTime < NodeStats.DEFAULT_SUB_MAX_PING_TIME && avgPingTime < NodeStats.DEFAULT_SUB_MAX_PING_TIME) { System.err.println("All nodes fully connected"); Logger.normal(RealNodeTest.class, "All nodes fully connected"); System.err.println(); return; } else { long tDelta = (System.currentTimeMillis() - tStart)/1000; System.err.println("Waiting for nodes to be fully connected: "+countFullyConnected+" / "+nodes.length+" ("+totalConnections+" / "+totalPeers+" connections total partial "+totalPartialConnections+" compatible "+totalCompatibleConnections+") - backed off "+totalBackedOff+" ping min/avg/max "+(int)minPingTime+"/"+(int)avgPingTime+"/"+(int)maxPingTime+" at "+tDelta+'s'); Logger.normal(RealNodeTest.class, "Waiting for nodes to be fully connected: "+countFullyConnected+" / "+nodes.length+" ("+totalConnections+" / "+totalPeers+" connections total partial "+totalPartialConnections+" compatible "+totalCompatibleConnections+") - backed off "+totalBackedOff+" ping min/avg/max "+(int)minPingTime+"/"+(int)avgPingTime+"/"+(int)maxPingTime+" at "+tDelta+'s'); Thread.sleep(1000); } } } }