/** * clientMain.cpp * - compiles to bin/client * - runs a client program for experiments with PRSONA * * Stan Gurtler */ #include #include #include "networkClient.hpp" using namespace std; /** * This program (bin/client) expects to be called as follows: * `bin/client ` * * - a name for this instance, which comes with a config file identifying * its IP address and port * - a string that will name the file in which outputs for this run of * the program will be written (that is, timings and traffic data) * - a positive integer that determines the absolute soundness parameter * for batched proofs * - a bool (given as T/t or F/f) * which is true when servers are in malicious security * and false when they are in HBC security */ int main(int argc, char *argv[]) { /* * PRELIMINARY SETUP CODE */ initialize_prsona_classes(); #if USE_SSL mg_init_library(MG_FEATURES_SSL); #else mg_init_library(0); #endif // The id effectively tells the client what ip/port it is string id = ""; if (argc > 1) id = argv[1]; string output = "default"; if (argc > 2) output = argv[2]; string outputDir = "out/" + output + "/" + id; int mkdirOut = system(("mkdir -p " + outputDir).c_str()); mutex voteOutputMtx; string voteOutputFilename = outputDir + "/vote.out"; mutex repProofOutputMtx; string repProofOutputFilename = outputDir + "/repProver.out"; mutex repVerifyOutputMtx; string repVerifyOutputFilename = outputDir + "/repVerifier.out"; mutex memUsageOutputMtx; string memUsageOutputFilename = outputDir + "/usage.out"; // Default to not doing proof batching if not specified size_t lambda = 0; if (argc > 3) lambda = atoi(argv[3]); if (lambda > 0) PrsonaBase::set_lambda(lambda); // Default to malicious security if not specified bool maliciousServers = true; if (argc > 4) maliciousServers = argv[4][0] == 't' || argv[4][0] == 'T'; // Set malicious flags where necessary if (maliciousServers) PrsonaBase::set_server_malicious(); // This seed is used to generate temp filenames and decide which server to make requests to at any step. string seedStr; if (id.empty()) seedStr = "default-client"; else { seedStr = id; seedStr += "-" + output; seedStr += "-client"; } seed_seq seed(seedStr.begin(), seedStr.end()); default_random_engine rng(seed); vector serverIPs, clientIPs; vector serverPorts, clientPorts; string selfIP, selfPortStr; int selfPort = 0; string configDir = "cfg/" + output; // Read in from config files the server locations load_multiple_instances_config(serverIPs, serverPorts, (configDir + "/serverIPs.cfg").c_str()); // And now the client locations load_multiple_instances_config(clientIPs, clientPorts, (configDir + "/clientIPs.cfg").c_str()); // Finally, read in the ip/port corresponding to the id that this instance was given string selfConfigFilename = configDir + "/selfIP"; if (!id.empty()) { selfConfigFilename += "-"; selfConfigFilename += id; } selfConfigFilename += ".cfg"; load_single_instance_config(selfIP, selfPortStr, selfPort, selfConfigFilename.c_str()); size_t numServers = serverIPs.size(); size_t numClients = clientIPs.size(); uniform_int_distribution distribution(0, numServers - 1); const char *options[] = {"listening_ports", selfPortStr.c_str(), 0}; CivetServer server(options); /* * CLIENT SETUP CODE */ struct synchronization_tool exitSync; unique_lock exitLock(exitSync.mtx); cout << "[" << seedStr << "] Establishing PRSONA client with the following parameters: " << endl; cout << "[" << seedStr << "] " << numServers << " PRSONA servers" << endl; cout << "[" << seedStr << "] " << numClients << " PRSONA clients" << endl; cout << "[" << seedStr << "] " << "Proof batching " << (lambda > 0 ? "IS" : "is NOT") << " in use." << endl; if (lambda > 0) cout << "[" << seedStr << "] Batch parameter: " << lambda << endl; cout << "[" << seedStr << "] Servers are set to " << (maliciousServers ? "MALICIOUS" : "HBC") << " security" << endl; cout << "[" << seedStr << "] This client is at IP address: " << selfIP << ":" << selfPort << endl; cout << endl; cout << "[" << seedStr << "] Creating PRSONA client object." << endl; PrsonaClient *prsonaClient = create_client(rng, serverIPs, serverPorts, numServers); cout << "[" << seedStr << "] Setting up handlers for client." << endl; // Main handler (in clients, only used for verifying reputation proofs) PrsonaClientWebSocketHandler wsHandler(rng, prsonaClient, serverIPs, serverPorts, repVerifyOutputMtx, repVerifyOutputFilename, memUsageOutputMtx, memUsageOutputFilename); server.addWebSocketHandler("/ws", wsHandler); // Exit handler (allows client to be brought down by making correct GET request) exitSync.val = 0; exitSync.val2 = 0; RemoteControlHandler exitHandler(&exitSync, "Client coming down!"); server.addHandler(EXIT_URI, exitHandler); // This handler tells the orchestrator when the client has finished an assigned task ClientReadyHandler clientReadyHandler(&exitSync); server.addHandler(CLIENT_READY_URI, clientReadyHandler); // Make-vote handler (allows orchestrator to cause this client to make a new, random vote) AltRemoteControlHandler triggerVoteHandler(CLIENT_MAKE_VOTE, &exitSync, "Client will make new votes!"); server.addHandler(TRIGGER_VOTE_URI, triggerVoteHandler); // Make-reputation handler (allows orchestrator to cause this client to make a new reputation proof, and specifies which other client to give it to) AltRemoteControlHandler triggerRepHandler(CLIENT_MAKE_REP_PROOF, &exitSync, "Client will make reputation proof!"); server.addHandler(TRIGGER_REP_URI, triggerRepHandler); /* * MAIN CLIENT LOOP CODE */ cout << "[" << seedStr << "] Entering main ready loop." << endl; while (!exitSync.val) { // exitSync.val controls when an exit is signaled. // exitSync.val2 controls when making a vote or making a reputation proof is signaled while (!exitSync.val && !exitSync.val2) exitSync.cv.wait(exitLock); size_t whichServer; string fullQuery, target; size_t colonLocation; int targetPort; // exitSync.val2 is set by the make-vote and make-reputation handlers, but will be 0 in the event of signaling an exit switch (exitSync.val2) { case CLIENT_MAKE_VOTE: cout << "[" << seedStr << "] Making new vote row." << endl; whichServer = distribution(rng); make_vote(rng, prsonaClient, serverIPs, serverPorts, serverIPs[whichServer], serverPorts[whichServer], numClients, server, voteOutputMtx, voteOutputFilename, memUsageOutputMtx, memUsageOutputFilename); cout << "[" << seedStr << "] New vote row complete." << endl; break; case CLIENT_MAKE_REP_PROOF: fullQuery = triggerRepHandler.getQuery(); colonLocation = fullQuery.find(":"); target = fullQuery.substr(0, colonLocation); targetPort = stoi(fullQuery.substr(colonLocation + 1)); cout << "[" << seedStr << "] Making new reputation proof." << endl; make_reputation_proof(rng, prsonaClient, serverIPs, serverPorts, target, targetPort, numClients, server, repProofOutputMtx, repProofOutputFilename, memUsageOutputMtx, memUsageOutputFilename); cout << "[" << seedStr << "] New reputation proof complete." << endl; break; default: // Occurs when an exit is signaled; do nothing break; } // Make sure to reset this, so that we wait until something new is signaled to do exitSync.val2 = 0; } /* * SHUTDOWN CODE */ cout << "[" << seedStr << "] Shutting down." << endl; mg_exit_library(); delete prsonaClient; return 0; }