/* 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 java.io.IOException;
import java.util.Arrays;
import java.util.ArrayList;
import java.util.List;
import freenet.crypt.DummyRandomSource;
import freenet.crypt.RandomSource;
import freenet.io.comm.PeerParseException;
import freenet.io.comm.ReferenceSignatureVerificationException;
import freenet.keys.CHKEncodeException;
import freenet.keys.ClientCHKBlock;
import freenet.keys.ClientKSK;
import freenet.keys.ClientKey;
import freenet.keys.ClientKeyBlock;
import freenet.keys.FreenetURI;
import freenet.keys.InsertableClientSSK;
import freenet.keys.KeyDecodeException;
import freenet.keys.SSKEncodeException;
import freenet.node.FSParseException;
import freenet.node.LowLevelGetException;
import freenet.node.Node;
import freenet.node.NodeInitException;
import freenet.node.NodeStarter;
import freenet.support.Executor;
import freenet.support.Logger;
import freenet.support.PooledExecutor;
import freenet.support.Logger.LogLevel;
import freenet.support.LoggerHook.InvalidThresholdException;
import freenet.support.compress.Compressor.COMPRESSOR_TYPE;
import freenet.support.compress.InvalidCompressionCodecException;
import freenet.support.io.ArrayBucket;
import freenet.support.io.FileUtil;
import freenet.support.math.RunningAverage;
import freenet.support.math.SimpleRunningAverage;
/**
* @author amphibian
*/
public class RealNodeRequestInsertTest extends RealNodeRoutingTest {
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 boolean ENABLE_SWAPPING = false;
static final boolean ENABLE_ULPRS = false;
static final boolean ENABLE_PER_NODE_FAILURE_TABLES = false;
static final boolean ENABLE_SWAP_QUEUEING = false;
static final boolean ENABLE_PACKET_COALESCING = true;
static final boolean ENABLE_FOAF = true;
static final boolean FORK_ON_CACHEABLE = false;
static final boolean DISABLE_PROBABILISTIC_HTLS = true;
// Set to true to cache everything. This depends on security level.
static final boolean USE_SLASHDOT_CACHE = false;
static final boolean REAL_TIME_FLAG = false;
static final int TARGET_SUCCESSES = 20;
//static final int NUMBER_OF_NODES = 50;
//static final short MAX_HTL = 10;
// FIXME: HACK: High bwlimit makes the "other" requests not affect the test requests.
// Real solution is to get rid of the "other" requests!!
static final int BWLIMIT = 1000*1024;
//public static final int DARKNET_PORT_BASE = RealNodePingTest.DARKNET_PORT2+1;
public static final int DARKNET_PORT_BASE = 10000;
public static final int DARKNET_PORT_END = DARKNET_PORT_BASE + NUMBER_OF_NODES;
public static void main(String[] args) throws FSParseException, PeerParseException, CHKEncodeException, InvalidThresholdException, NodeInitException, ReferenceSignatureVerificationException, InterruptedException {
String name = "realNodeRequestInsertTest";
File wd = new File(name);
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(name, false, LogLevel.ERROR, "freenet.node.Location:normal,freenet.node.simulator.RealNode:minor,freenet.node.Insert:MINOR,freenet.node.Request:MINOR,freenet.node.Node:MINOR");
//NodeStarter.globalTestInit(name, false, LogLevel.ERROR, "freenet.node.Location:MINOR,freenet.io.comm:MINOR,freenet.node.NodeDispatcher:MINOR,freenet.node.simulator:MINOR,freenet.node.PeerManager:MINOR,freenet.node.RequestSender:MINOR");
//NodeStarter.globalTestInit(name, false, LogLevel.ERROR, "freenet.node.FNP:MINOR,freenet.node.Packet:MINOR,freenet.io.comm:MINOR,freenet.node.PeerNode:MINOR,freenet.node.DarknetPeerNode:MINOR");
NodeStarter.globalTestInit(name, false, LogLevel.ERROR, "", true);
System.out.println("Insert/retrieve test");
System.out.println();
DummyRandomSource random = new DummyRandomSource(3142);
DummyRandomSource topologyRandom = new DummyRandomSource(3143);
//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++) {
nodes[i] =
NodeStarter.createTestNode(DARKNET_PORT_BASE+i, 0, name, DISABLE_PROBABILISTIC_HTLS, MAX_HTL, 20 /* 5% */, random, executor, 500*NUMBER_OF_NODES, 256*1024, true, ENABLE_SWAPPING, false, ENABLE_ULPRS, ENABLE_PER_NODE_FAILURE_TABLES, ENABLE_SWAP_QUEUEING, ENABLE_PACKET_COALESCING, BWLIMIT, ENABLE_FOAF, false, true, USE_SLASHDOT_CACHE, null);
Logger.normal(RealNodeRoutingTest.class, "Created node "+i);
}
// Now link them up
makeKleinbergNetwork(nodes, START_WITH_IDEAL_LOCATIONS, DEGREE, FORCE_NEIGHBOUR_CONNECTIONS, topologyRandom);
Logger.normal(RealNodeRoutingTest.class, "Added random links");
for(int i=0;i<NUMBER_OF_NODES;i++) {
nodes[i].start(false);
System.err.println("Started node "+i+"/"+nodes.length);
}
waitForAllConnected(nodes);
waitForPingAverage(0.5, nodes, new DummyRandomSource(3143), MAX_PINGS, 1000);
random = new DummyRandomSource(3144);
System.out.println();
System.out.println("Ping average > 95%, lets do some inserts/requests");
System.out.println();
RealNodeRequestInsertTest tester = new RealNodeRequestInsertTest(nodes, random, TARGET_SUCCESSES);
while(true) {
try {
waitForAllConnected(nodes);
int status = tester.insertRequestTest();
if(status == -1) continue;
System.exit(status);
} catch (Throwable t) {
Logger.error(RealNodeRequestInsertTest.class, "Caught "+t, t);
}
}
}
public RealNodeRequestInsertTest(Node[] nodes, DummyRandomSource random, int targetSuccesses) {
this.nodes = nodes;
this.random = random;
this.targetSuccesses = targetSuccesses;
}
private final Node[] nodes;
private final RandomSource random;
private int requestNumber = 0;
private RunningAverage requestsAvg = new SimpleRunningAverage(100, 0.0);
private String baseString = System.currentTimeMillis() + " ";
private int insertAttempts = 0;
private int fetchSuccesses = 0;
private final int targetSuccesses;
/**
* @param nodes
* @param random
* @return -1 to continue or an exit code (0 or positive for an error).
* @throws CHKEncodeException
* @throws InvalidCompressionCodecException
* @throws SSKEncodeException
* @throws IOException
* @throws KeyDecodeException
*/
int insertRequestTest() throws CHKEncodeException, InvalidCompressionCodecException, SSKEncodeException, IOException, KeyDecodeException {
requestNumber++;
try {
Thread.sleep(100);
} catch (InterruptedException e1) {
}
String dataString = baseString + requestNumber;
// Pick random node to insert to
int node1 = random.nextInt(NUMBER_OF_NODES);
Node randomNode = nodes[node1];
//Logger.error(RealNodeRequestInsertTest.class,"Inserting: \""+dataString+"\" to "+node1);
//boolean isSSK = requestNumber % 2 == 1;
boolean isSSK = true;
FreenetURI testKey;
ClientKey insertKey;
ClientKey fetchKey;
ClientKeyBlock block;
byte[] buf = dataString.getBytes("UTF-8");
if(isSSK) {
testKey = new FreenetURI("KSK", dataString);
insertKey = InsertableClientSSK.create(testKey);
fetchKey = ClientKSK.create(testKey);
block = ((InsertableClientSSK)insertKey).encode(new ArrayBucket(buf), false, false, (short)-1, buf.length, random, COMPRESSOR_TYPE.DEFAULT_COMPRESSORDESCRIPTOR, false);
} else {
block = ClientCHKBlock.encode(buf, false, false, (short)-1, buf.length, COMPRESSOR_TYPE.DEFAULT_COMPRESSORDESCRIPTOR, false);
insertKey = fetchKey = block.getClientKey();
testKey = insertKey.getURI();
}
System.err.println();
System.err.println("Created random test key "+testKey+" = "+fetchKey.getNodeKey(false));
System.err.println();
byte[] data = dataString.getBytes("UTF-8");
Logger.minor(RealNodeRequestInsertTest.class, "Decoded: "+new String(block.memoryDecode(), "UTF-8"));
Logger.normal(RealNodeRequestInsertTest.class,"Insert Key: "+insertKey.getURI());
Logger.normal(RealNodeRequestInsertTest.class,"Fetch Key: "+fetchKey.getURI());
try {
insertAttempts++;
randomNode.clientCore.realPut(block.getBlock(), false, FORK_ON_CACHEABLE, false, false, REAL_TIME_FLAG);
Logger.error(RealNodeRequestInsertTest.class, "Inserted to "+node1);
} catch (freenet.node.LowLevelPutException putEx) {
Logger.error(RealNodeRequestInsertTest.class, "Insert failed: "+ putEx);
System.err.println("Insert failed: "+ putEx);
return EXIT_INSERT_FAILED;
}
// Pick random node to request from
int node2;
do {
node2 = random.nextInt(NUMBER_OF_NODES);
} while(node2 == node1);
Node fetchNode = nodes[node2];
try {
block = fetchNode.clientCore.realGetKey(fetchKey, false, false, false, REAL_TIME_FLAG);
} catch (LowLevelGetException e) {
block = null;
}
if(block == null) {
int percentSuccess=100*fetchSuccesses/insertAttempts;
Logger.error(RealNodeRequestInsertTest.class, "Fetch #"+requestNumber+" FAILED ("+percentSuccess+"%); from "+node2);
System.err.println("Fetch #"+requestNumber+" FAILED ("+percentSuccess+"%); from "+node2);
requestsAvg.report(0.0);
} else {
byte[] results = block.memoryDecode();
requestsAvg.report(1.0);
if(Arrays.equals(results, data)) {
fetchSuccesses++;
int percentSuccess=100*fetchSuccesses/insertAttempts;
Logger.error(RealNodeRequestInsertTest.class, "Fetch #"+requestNumber+" from node "+node2+" succeeded ("+percentSuccess+"%): "+new String(results));
System.err.println("Fetch #"+requestNumber+" succeeded ("+percentSuccess+"%): \""+new String(results)+'\"');
if(fetchSuccesses == targetSuccesses) {
System.err.println("Succeeded, "+targetSuccesses+" successful fetches");
return 0;
}
} else {
Logger.error(RealNodeRequestInsertTest.class, "Returned invalid data!: "+new String(results));
System.err.println("Returned invalid data!: "+new String(results));
return EXIT_BAD_DATA;
}
}
StringBuilder load = new StringBuilder("Running UIDs for nodes: ");
int totalRunningUIDsAlt = 0;
List<Long> runningUIDsList = new ArrayList<Long>();
for(int i=0;i<nodes.length;i++) {
load.append(i);
load.append(':');
nodes[i].tracker.addRunningUIDs(runningUIDsList);
int runningUIDsAlt = nodes[i].tracker.getTotalRunningUIDsAlt();
totalRunningUIDsAlt += runningUIDsAlt;
load.append(totalRunningUIDsAlt);
if(i != nodes.length-1)
load.append(' ');
}
System.err.println(load.toString());
if(totalRunningUIDsAlt != 0)
System.err.println("Still running UIDs (alt): "+totalRunningUIDsAlt);
if(!runningUIDsList.isEmpty()) {
System.err.println("List of running UIDs: "+Arrays.toString(runningUIDsList.toArray()));
}
return -1;
}
}