/** * serverMain.cpp * - compiles to bin/server * - runs a server program for experiments with PRSONA * * Stan Gurtler */ #include #include #include "networkServer.hpp" using namespace std; /** * This program (bin/server) expects to be called as follows: * `bin/server ` * * - 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 server what ip/port it is string id = ""; if (argc > 1) id = argv[1]; string output = "default"; if (argc > 2) output = argv[2]; string outputDir = "/home/tmgurtle/prsona/prsona/out/" + output + "/" + id; int mkdirOut = system(("mkdir -p " + outputDir).c_str()); mutex epochBuildUpOutputMtx; string epochBuildUpOutputFilename = outputDir + "/epochUp.out"; mutex epochBreakDownOutputMtx; string epochBreakDownOutputFilename = outputDir + "/epochDown.out"; mutex epochUpdateOutputMtx; string epochUpdateOutputFilename = outputDir + "/epochUpdate.out"; mutex voteUpdateOutputMtx; string voteUpdateOutputFilename = outputDir + "/voteUpdate.out"; mutex fullEpochOutputMtx; string fullEpochOutputFilename = outputDir + "/overallEpoch.out"; mutex memUsageOutputMtx; string memUsageOutputFilename = outputDir + "/usage.out"; // Default to no proof batching if not specified size_t lambda = 0; if (argc > 3) lambda = atoi(argv[3]); // Set lambda where necessary 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. string seedStr; if (id.empty()) seedStr = "default-server"; else { seedStr = id; seedStr += "-" + output; seedStr += "-server"; } seed_seq seed(seedStr.begin(), seedStr.end()); default_random_engine rng(seed); vector serverIPs; vector serverPorts; string selfIP, selfPortStr, dealerIP, dealerPortStr; int selfPort = 0, dealerPort = 0; string configDir = "/home/tmgurtle/prsona/prsona/cfg/" + output; // Read in from config files the server locations load_multiple_instances_config(serverIPs, serverPorts, (configDir + "/serverIPs.cfg").c_str()); // 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()); // And finally the dealer location load_single_instance_config(dealerIP, dealerPortStr, dealerPort, (configDir + "/dealerIP.cfg").c_str()); size_t numServers = serverIPs.size(); bool bgnDealer = selfIP == dealerIP && selfPort == dealerPort; struct synchronization_tool exitSync, readySync; mutex updateMtx; unique_lock *updateLockHolder = NULL; atomic epochNum(0); const char *options[] = {"listening_ports", selfPortStr.c_str(), 0}; CivetServer server(options); /* * SERVER SETUP CODE */ cout << "[" << seedStr << "] Establishing PRSONA server with the following parameters: " << endl; cout << "[" << seedStr << "] " << numServers << " PRSONA servers" << endl; cout << "[" << seedStr << "] This server " << (bgnDealer ? "IS" : "is NOT") << " the trusted BGN dealer" << 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 server is at IP address: " << selfIP << ":" << selfPort << endl; cout << "[" << seedStr << "] The BGN dealer is at IP address: " << dealerIP << ":" << dealerPort << endl; cout << endl; cout << "[" << seedStr << "] Creating PRSONA server object." << endl; PrsonaServer *prsonaServer = create_server(rng, dealerIP, dealerPort, bgnDealer, numServers); cout << "[" << seedStr << "] Setting up handlers for server." << endl; // Main handler PrsonaServerWebSocketHandler wsHandler(rng, prsonaServer, serverIPs, serverPorts, selfIP, selfPort, updateMtx, epochNum, epochBuildUpOutputMtx, epochBuildUpOutputFilename, epochBreakDownOutputMtx, epochBreakDownOutputFilename, epochUpdateOutputMtx, epochUpdateOutputFilename, voteUpdateOutputMtx, voteUpdateOutputFilename, memUsageOutputMtx, memUsageOutputFilename); server.addWebSocketHandler("/ws", wsHandler); // Exit handler (allows server to be brought down by making correct GET request) unique_lock exitLock(exitSync.mtx); exitSync.val = 0; exitSync.val2 = 0; RemoteControlHandler exitHandler(&exitSync, "Server coming down!"); server.addHandler(EXIT_URI, exitHandler); // Update mutex handler (allows servers to synchronize updates, like epochs and new users) UpdateLockWebSocketHandler lockHandler(updateMtx, &updateLockHolder, true); UpdateLockWebSocketHandler unlockHandler(updateMtx, &updateLockHolder, false); server.addWebSocketHandler(UPDATE_LOCK_URI, lockHandler); server.addWebSocketHandler(UPDATE_UNLOCK_URI, unlockHandler); // Epoch number handler (allows a server to indicate what epoch the system is on) EpochNumHandler epochNumHandler(epochNum); server.addHandler(WHICH_EPOCH_URI, epochNumHandler); // This handler (to determine system main loop readiness) is only used by the dealer RemoteControlHandler serverReadyHandler(&readySync, "ACK"); // Similarly, this handler (to tell the orchestrator when epochs are ready to happen) is only used by the dealer EpochReadyHandler epochReadyHandler(&exitSync, &readySync, updateMtx, numServers); // Make-epoch trigger (which causes the dealer to initiate an epoch change) AltRemoteControlHandler triggerEpochHandler(1, &exitSync, "Server will initiate epoch!"); // It's probably best to give them all a lifetime as long as the CivetWeb server object if (bgnDealer) // The dealer needs to wait for the other servers to check in, because it needs to lead forming the first fresh generator { // The readySync lock is only used here, while we're waiting for other servers to check in unique_lock lck(readySync.mtx); readySync.val = 1; server.addHandler(SERVER_CHECK_IN_URI, serverReadyHandler); // In addition, there are also a few extra handlers only the dealer needs server.addHandler(TRIGGER_EPOCH_URI, triggerEpochHandler); server.addHandler(EPOCH_READY_URI, epochReadyHandler); cout << "[" << seedStr << "] Waiting for other servers to retrieve BGN details and check in." << endl; // Wait for the other servers to check in while (readySync.val < numServers) { cout << "[" << seedStr << "] " << readySync.val << " servers are checked in." << endl; readySync.cv.wait(lck); } // Once we're ready, form and distribute the first fresh generator and the H used in ElGamal operations initiate_generators(rng, prsonaServer, serverIPs, serverPorts, selfIP, selfPort); } else // Non-dealer servers need to check in with the dealer { cout << "[" << seedStr << "] Checking in with the BGN dealer." << endl; check_in_with_dealer(dealerIP, dealerPort); } /* * MAIN SERVER LOOP CODE */ cout << "[" << seedStr << "] Entering main ready loop." << endl; if (bgnDealer) { while (!exitSync.val) { // exitSync.val controls when an exit is signaled. // exitSync.val2 controls when an epoch change is signaled while (!exitSync.val && !exitSync.val2) exitSync.cv.wait(exitLock); // exitSync.val2 is set by the make-epoch handler, and will be 0 in the event of signaling an exit if (exitSync.val2) { size_t currEpoch = epochNum.load(); cout << "[" << seedStr << "] Executing epoch calculations (going from t = " << currEpoch << " to " << currEpoch + 1 << ")." << endl; make_epoch(rng, prsonaServer, serverIPs, serverPorts, selfIP, selfPort, updateMtx, epochNum, server, epochBuildUpOutputMtx, epochBuildUpOutputFilename, epochBreakDownOutputMtx, epochBreakDownOutputFilename, fullEpochOutputMtx, fullEpochOutputFilename, memUsageOutputMtx, memUsageOutputFilename); currEpoch = epochNum.load(); cout << "[" << seedStr << "] Epoch calculations complete (now in t = " << currEpoch << ")." << endl; } // Make sure to reset this, so that we wait until something new is signaled to do exitSync.val2 = 0; } } else { // exitSync.val controls when an exit is signaled. // exitSync.val2 is not used by non-dealer servers. while (!exitSync.val) exitSync.cv.wait(exitLock); } /* * SHUTDOWN CODE */ cout << "[" << seedStr << "] Shutting down." << endl; mg_exit_library(); delete prsonaServer; return 0; }