/* Copyright (C) 2014 Carlos Aguilar Melchor, Joris Barrier, Marc-Olivier Killijian
* This file is part of XPIR.
*
* XPIR is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* XPIR is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with XPIR. If not, see .
*/
#include "PIROptimizer.hpp"
// Set this to true if the server database is stored in precomputed (e.g. NTT) form
// TODO this should be an option given on the command line
#define INITIAL_PRECOMPUTATION_DONE false
static const unsigned int kPrecision = 5;
const unsigned int PIROptimizer::kalphaBound = 100;
/**
* Class constructor.
* Params :
* - pirClient_ptr client : PIRClient shared_ptr ;
* - int security_bits : security bits wanted ;
* - string server_ip : IPv4 server adresse ;
* - io_service& ios : boost io_service reference, used for boost socket ;
**/
PIROptimizer::PIROptimizer(string server_ip_, int port_, FitnessType fitnessMethod_) :
serv_ip(server_ip_),
server_optimport(port_),
s(ios),
// Number of clients the servers would like to handle in parallel : Fixed for the moment (the server will tell in future versions)
nbc(1),
optimum(fitnessMethod_),
silent(false)
{
// Max latency for the socket when connecting to server
struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
setsockopt(s.native(), SOL_SOCKET, SO_RCVTIMEO, &tv, sizeof(tv));
setsockopt(s.native(), SOL_SOCKET, SO_SNDTIMEO, &tv, sizeof(tv));
}
PIROptimizer::PIROptimizer():
s(ios),
nbc(1),
silent(false)
{}
OptimVars PIROptimizer::optimizeFromServer(FixedVars& partial_fixed_vars, boost::asio::ip::tcp::socket& socket)
{
fixedVars = partial_fixed_vars;
getFixedVarsFromServer(fixedVars, socket);
getNetworkInfos(fixedVars, socket);
optimize(socket, fixedVars, 0);
sendExitCommand(socket);
return global_optimum;
}
void PIROptimizer::optimize(FixedVars& fixed_vars, unsigned int exp_nbr)
{
try
{
s.connect(tcp::endpoint(boost::asio::ip::address::from_string(serv_ip), server_optimport));
// Tell server we are an optimizer not a client
int is_client = 0;
write(s, boost::asio::buffer(&is_client, sizeof(int)));
}
catch (const std::exception& ex)
{
std::cout << "Optimizer: Unable to connect to "<< serv_ip << " port " << server_optimport < crypto_params_desc_set;
vector crypto_systems_vec;
// Get all the crypto modules
HomomorphicCryptoFactory_internal::getAllCryptoSystems(crypto_systems_vec);
if(fixedVars.manual_crypto_params == "")
{
noCryptoSystemOptim(exp_nbr);
}
else
{
std::cout << "Optimizer: Crypto parameters manually forced to " << fixedVars.manual_crypto_params << std::endl;
// Get a regular expression from the requested params
cp_rex = new std::regex(fixedVars.manual_crypto_params);
// Only take into account NoCryptography as a possibility if requested
if(std::regex_match("NoCryptography", *cp_rex))
{
noCryptoSystemOptim(exp_nbr);
}
}
// else
// {
// std::vector fields;
// boost::algorithm::split(fields, fixedVars.manual_crypto_params, boost::algorithm::is_any_of(":"));
// HomomorphicCrypto* h;
//
// h = HomomorphicCryptoFactory_internal::getCrypto(fields[0]);
// crypto_systems_vec.push_back(h);
// }
// }
// Iterate on all crypto modules
for (auto crypto_system_ptr : crypto_systems_vec)
{
crypto = crypto_system_ptr;
//Get i-th crypto module's public parameter
currentPublicParams = &crypto->getPublicParameters();
// Get all the proposed parameter sets for the requested security on this crypto module
crypto->getCryptoParams(fixedVars.k, crypto_params_desc_set);
// If special parameters requested, remove all those that don't match the regexp
if(fixedVars.manual_crypto_params != "")
{
const set crypto_params_desc_set_copy=crypto_params_desc_set;
for (auto cp_ptr : crypto_params_desc_set_copy)
{
if (!std::regex_match(cp_ptr, *cp_rex)) crypto_params_desc_set.erase(cp_ptr);
//HomomorphicCrypto* h;
//h = HomomorphicCryptoFactory_internal::getCrypto(fields[0]);
//crypto_systems_vec.push_back(h);
}
if (crypto_params_desc_set.size() == 0) continue;
//crypto_params_desc_set.insert(fixedVars.manual_crypto_params);
}
// Add to cost dictionnaries (abs_cache and precompute_cache) estimates from server
// or pre-established values if no server available
getAbsAndPreCachesFromServer(s, crypto);
// Add to cost dictionnaries (encryptcache, decryptcache) cost estimates (pot. precomputed)
getPreComputedData(crypto);
// Iterate on all the parameters for this crypto module
for (std::string crypto_params_desc : crypto_params_desc_set)
{
// Set the current public parameters to the parameter set values
currentPublicParams->computeNewParameters(crypto_params_desc);
// Iterate on the dimensions
for (unsigned int d = fixedVars.dMin ; d <= fixedVars.dMax ; d++)
{
// Define a OptimVars object (with the variables that we can choose for optimization)
OptimVars vars(fixedVars.fitness, fixedVars);
vars.crypto_params = crypto_params_desc;
// Get best alpha for this iteration given the fitness method and set the variables in vars accordingly
// - First do a test with no aggreggation
findAlpha(d, 1, 1, vars, exp_nbr);
// - Then search with dichotomy
findAlphaDicho(0, getMaxAlpha(), d, exp_nbr, vars);
}
}
// Show and write the optimized values.
processResults(exp_nbr);
// Save best optimum inter cryptosystems
std::cout << "Optimizer: Comparing (" << optimum.crypto_params << ", " << optimum.getValue() << ") with (" << global_optimum.crypto_params << ", " << global_optimum.getValue() << ")" << std::endl;
if(optimum < global_optimum)
{
global_optimum = optimum;
}
std::cout << "Optimizer: Winner is (" << global_optimum.crypto_params << ", " << global_optimum.getValue() << ")" << std::endl;
// Clean
optimum.reset();
crypto_params_desc_set.clear();
delete crypto;
}
disconnect();
// If nothing was found say it and exit
if (global_optimum.crypto_params == "No crypto params")
{
std::cout << "Optimizer: No valid crypto parameters matching " << fixedVars.manual_crypto_params << " found, exiting ..." << std::endl;
exit(0);
}
}
/**
* Set optimum with values giving by a trivial PIR. (i.e download the entiere database)
**/
void PIROptimizer::noCryptoSystemOptim(unsigned int exp_nbr)
{
optimum.crypto_params = "NoCryptography";
optimum.setFixedVars(fixedVars);
optimum.setGenQ(0);
optimum.setSendQ(0);
optimum.setGenR(0);
optimum.setSendR((fixedVars.l * fixedVars.n) / fixedVars.Tdoc);
optimum.setDecR(0);
optimum.setDim(1);
optimum.setAlpha(fixedVars.n);
if (silent == false) showBestResults(exp_nbr);
// Save this as the best result inter cryptosystems
global_optimum = optimum;
optimum.reset();
}
/**
* Find Iteratively the best alpha and set the optimal variables in var
**/
void PIROptimizer::findAlpha(unsigned int d, OptimVars& vars, unsigned int exp_nbr)
{
// alphaMax == 0 is a convention to note that no limit should be done in aggregation
unsigned int alpha_max = (fixedVars.alphaMax == 0) ? fixedVars.n : fixedVars.alphaMax;
//Really get best alpha between 0 and alpha_max
findAlpha(d, 0, alpha_max, vars, exp_nbr);
}
void PIROptimizer::findAlpha(unsigned int d, unsigned int alpha_min, unsigned int alpha_max, OptimVars& vars, unsigned int exp_nbr)
{
// Number of bits that can be absorbed by a ciphertext for our parameters
long abs_bit;
// If aggregation is done, try to use all plaintext space
unsigned int minimum_reasonable_alpha = getMinAlpha();
// Iterate on possible aggregation values
for (unsigned int current_alpha = alpha_min ; current_alpha <= alpha_max ; current_alpha+=minimum_reasonable_alpha)
{
// In first iteration current_alpha=0 is treated as 1 (i.e. no aggregation)
computeOptimVars(current_alpha, d, vars);
// If no bits can be absorbed for this choice ignore it
abs_bit = crypto->getPublicParameters().getAbsorptionBitsize();
if(abs_bit <= 0)
{
cout << "PIROptimizer: Unusable cryptoparams, ignoring them" << endl;
break;
}
// Write test result to output file
OptimService::writeTestCurrentResult(1, alpha_max, current_alpha, 1, alpha_max, d, exp_nbr, vars);
}
}
void PIROptimizer::findAlphaDicho(unsigned int inf, unsigned int sup, unsigned int d, unsigned int exp_nbr, OptimVars& vars)
{
unsigned int min_alpha = getMinAlpha();
//When the space to explore is relatively little we use an iterative method to steer clear of local optima.
if ((sup - inf) <= 100)
{
unsigned int max_alpha_bound = 0;
if (fixedVars.n < (sup * min_alpha))
max_alpha_bound = fixedVars.n;
else
max_alpha_bound = min(fixedVars.alphaMax, max(kalphaBound, sup * min_alpha));
findAlpha(d, inf * min_alpha, max_alpha_bound, vars, exp_nbr);
return;
}
//Next value to test.
unsigned int alpha_bound = inf + (sup - inf) / 2.0;
//Return the state of the slope (up or down).
int val = slop(alpha_bound, inf, sup, d, vars, exp_nbr);
//Write test restult to output file.
OptimService::writeTestCurrentResult(1, sup*getMinAlpha(), alpha_bound*getMinAlpha(), inf*getMinAlpha(), sup*getMinAlpha(), d, exp_nbr, vars);
if(val == -1) {
findAlphaDicho(alpha_bound, sup, d, exp_nbr, vars);
return;
}
findAlphaDicho(inf, alpha_bound, d, exp_nbr, vars);
}
int PIROptimizer::slop(unsigned int alphaMul, unsigned int inf, unsigned int sup, unsigned int d, OptimVars& vars, unsigned int exp_nbr)
{
// Try ten uniformly spaced values ahead to try to find a better result
unsigned int deltaright = (sup-1-alphaMul)/10;
unsigned int deltaleft = (alphaMul - inf-1)/10;
unsigned int minalpha = getMinAlpha();
double vright, vleft, vtmp;
// Ten increasing tests take the best
// ten decreasings tests take the best
// compare the two bests to determine the slope
// each side must have alphaMul+-1 at least
vright = computeOptimVars((alphaMul + 1) * minalpha, d, vars);
for (unsigned int i = 1 ; i <= 10 ; i++)
{
vtmp = computeOptimVars((alphaMul + 1 + i * deltaright) * minalpha, d, vars);
if (vtmp <= vright) vright = vtmp;
}
vleft = computeOptimVars((alphaMul - 1) * minalpha, d, vars);
for (unsigned int i = 1 ; i <= 10 ; i++)
{
vtmp = computeOptimVars((alphaMul - 1 - i * deltaleft) * minalpha, d, vars);
if (vtmp <= vleft) vleft = vtmp;
}
return (vright < vleft - 10e-8) ? -1 : 1 ;
}
/**
* Compute dimension sizes for a d-dimensional hypercube of n elements.
* Params:
* - unsigned int n: the number of elements
* - unsigned int alpha: the aggregation value
* - unsigned int d: the dimension
* - unsigned int *dn: computed dimension sizes (output)
**/
void PIROptimizer::getDimSize(unsigned int n, unsigned int alpha, unsigned int d, unsigned int *dn)
{
unsigned int prod = 1, j = 0;
// Elements in the database after the aggregation
unsigned int new_n = ceil(static_cast(n)/static_cast(alpha)); //PAtch tres sale à reprendre
// Dimension sizes. Product must be > n/alpha
unsigned int factors[d];
// Lower bound on the dimensions sizes needed. Correct only if n/alpha is a d-power.
for (unsigned int i = 0 ; i < d ; i++) factors[i] = floor(pow(new_n,1./d));
// If n/alpha is not a d-power
if (static_cast(factors[0]) != pow(new_n, static_cast(1.0 / static_cast(d))) )
{
// Increment each factor by one until the product of the dimension sizes is above n/alpha
while (prod < new_n && j < d)
{
prod = 1;
factors[j++]++;
for (unsigned int i = 0 ; i < d ; i++)
prod *= factors[i];
}
}
// Copy the result to the output
memcpy(dn, factors, sizeof(factors));
}
/**
* Compute time needed to send the request.
* Params:
* - double Tupc : client upload throughput
* - double Tdos : server download throughput
* - unsigned int nbc : number of clients that share server throughput (currently always 1);
* - unsigned int d : number of dimensions used for recursion
*
* Return:
* - double : time in seconds to send the PIR query.
**/
double PIROptimizer::getSendCost(double Tupc, double Tdos, unsigned int nbc, unsigned int d, unsigned int* dn)
{
// Available throughput for the transfer
double min_throughput = min(Tupc, Tdos/nbc );
// Time needed to send the query
double SenQ = 0.0;
// Sum the times needed for the subqueries of each dimension in the recursion
for (unsigned int i = 0 ; i < d ; i++)
{
// For a given dimension time = size / throughput and size = (nb_elements * size_of_an_element)
SenQ += (dn[i] * Tcq(i)) / min_throughput;
}
//std::cout << "dn[0]="<(fixedVars.l * alpha);
}
return ceil( static_cast(eltSize(alpha, i - 1) / Tp(i-1) )) * Tcr(i-1);
}
double PIROptimizer::Tp(unsigned int i)
{
return currentPublicParams->getAbsorptionBitsize(i);
}
/**
* Compute decryption cost.
* Params :
* - unsigned int d : dimension ;
* - unsigned int Tmi : encrypted size.
*
* Return :
* - double : time in seconds to decrypt something with given paramtes.
**/
double PIROptimizer::getDecryptCost(unsigned int alpha, unsigned int d)
{
double DecR = 0;
for (unsigned int i = 0 ; i < d ; i++)
{
// Yet another strange formula
DecR += ceill((eltSize(alpha, i + 1) / Tcr(i))) * getDecCost();
}
return DecR;
}
/**
* Return the size of reply for dimension "i"
**/
inline double PIROptimizer::Tcr(unsigned i)
{
return currentPublicParams->getCiphBitsizeFromRecLvl(i+1);
}
/**
* Return the size of a query for dimension "i"
**/
inline double PIROptimizer::Tcq(unsigned i)
{
return Tcr(i);
}
/**
* Return the time in secondes to decrypt data at dimension "i"
**/
double PIROptimizer::getDecCost(unsigned int d, crypto_ptr crypto)
{
PIRParameters pirParams; //Works for d = 1
pirParams.d = 1;
pirParams.alpha = 1;
unsigned int chunks = 1;
double start, stop, elapsed_time = 0;
// Needed for calls to getAbsorptionBitsize
crypto->setandgetAbsBitPerCiphertext(1); // Set internally best absorption possible
// Use a special function to generate a ciphertext to be sure its decryption cost is average
char* encrypted_data = (char*) crypto->encrypt_perftest();
PIRReplyExtraction_internal replyExt(pirParams,*crypto);
shared_queue clearChunks("clear_chunks");
do{
chunks *= 2;
for(unsigned int i = 0 ; i < chunks ; i++) //Fill, reply buffer with copy of "encrypted_data".
{
char* encrypted_data_copy = (char*) malloc((crypto->getPublicParameters().getCiphertextBitsize()/8) * sizeof(char));
memcpy(encrypted_data_copy, encrypted_data, crypto->getPublicParameters().getCiphertextBitsize()/8);
replyExt.repliesBuffer.push(encrypted_data_copy);
}
start = omp_get_wtime();
replyExt.extractReply((currentPublicParams->getAbsorptionBitsize()/8) * chunks, &clearChunks); //Do a PIR data extraction.
stop = omp_get_wtime();
while(!clearChunks.empty())
free(clearChunks.pop_front()); //free clear data.
}while((elapsed_time = (stop - start)) < 0.20);
double result = elapsed_time / chunks;
return result;
}
double PIROptimizer::getDecCost()
{
bool shortversion=true;
return decrypt_cache[currentPublicParams->getSerializedParams(shortversion)];
}
/**
* Compute SendR.
* Params :
* - double Tups : server upload time/bit ;
* - double Tdoc : client download time/bit;
* - unsigned int nbc : number of client ;
* - unsigned int Tmi : encrypted size.
* Return :
* - double : Duration in seconds.
**/
double PIROptimizer::getReceiveCost(unsigned int alpha, double Tups, double Tdoc, unsigned int nbc, unsigned int d)
{
// This is a bad clone of eltSize. To be removed.
//double SendR = eltSize(alpha, 0);
//
//for (unsigned int i = 0 ; i < d; i++)
//{
// SendR = ceil(SendR / Tp(i)) * Tcr(i);
//}
//SendR /= min(Tups/nbc, Tdoc);
//
//return SendR;
// eltSize computes how db elements grow with recursion.
// Reply size is what would be the element size for the d-th recursion (rec levels start at 0)
return eltSize(alpha,d)/min(Tups/nbc, Tdoc);
}
/**
* Get absorption time for 1 bit.
* Param :
* - unsigned int d : dimension.
**/
void PIROptimizer::getAbsAndPreCachesFromServer(boost::asio::ip::tcp::socket& s, crypto_ptr crypto_system)
{
size_t cmd = ABS;
try
{
write(s, boost::asio::buffer(&cmd, sizeof(size_t)));
std::string crypto_name = crypto_system->toString();
unsigned int crypto_name_size = crypto_name.size();
std::cout << "Optimizer: Requesting absorption and precomputation costs file for " << crypto_name << std::endl;
write(s, boost::asio::buffer(&crypto_name_size, sizeof(int)));
write(s, boost::asio::buffer(crypto_name));
int file_content_size = 0;
read(s, boost::asio::buffer(&file_content_size, sizeof(int)));
#ifdef DEBUG
std::cout << "Optimizer: File contents size " << file_content_size << std::endl;
#endif
char file_content[file_content_size + 1];
file_content_size = read(s, boost::asio::buffer(file_content, file_content_size));
file_content[file_content_size] = '\0';
makeAbsAndPrecomputeCaches(file_content);
}
catch(std::exception const& ex)
{
cout << "Optimizer: No server, using reference values for " << crypto_system->toString() << endl;
abs_cache.clear();
precompute_cache.clear();
set crypto_params_set;
crypto_system->getAllCryptoParams(crypto_params_set);
for (auto crypto_param : crypto_params_set)
{
abs_cache[crypto_param] = crypto_system->estimateAbsTime(crypto_param);
precompute_cache[crypto_param] = crypto_system->estimatePrecomputeTime(crypto_param);
}
}
}
void PIROptimizer::makeAbsAndPrecomputeCaches(char* serialized_cache)
{
abs_cache.clear();
precompute_cache.clear();
std::vector lines;
boost::algorithm::split(lines, serialized_cache, boost::algorithm::is_any_of("\n"));
for (auto line : lines)
{
std::cout << line << std::endl;
vector fields;
boost::algorithm::split(fields, line, boost::algorithm::is_any_of(" "));
if (fields.size() == 3)
{
abs_cache[fields.at(0)] = atof(fields.at(1).c_str());
precompute_cache[fields.at(0)] = atof(fields.at(2).c_str());
}
else if (line.find_first_not_of(' ') != std::string::npos)// if it is not a blank line
{
std::cout << "PIROptimizer: Absorption and precompute cache corrupted" << std::endl;
std::cout << "PIROptimizer: Remove files in exp/*.abs in the server before proceeding" << std::endl;
exit(1);
}
}
}
/**
* Compute the time for generate the complete query.
* Params :
* - unsigned int d : dimension ;
* - crypto_ptr crypto : HomomorphicCrypto shared_ptr ;
*
* Return :
* - double : Complete query duration.
**/
double PIROptimizer::getGenQueryCost(unsigned int d, unsigned int *dn)
{
double GenQ = 0.0;
bool shortversion = true;
string current_crypto_params = currentPublicParams->getSerializedParams(shortversion);
for (unsigned int i = 0 ; i < d ; i++)
{
GenQ += dn[i] * encrypt_cache[current_crypto_params];
}
return GenQ;
}
/**
* Get time to encrypt a query for a given dimension.
* Params :
* - unsigned int d : dimension ;
* - crypto_ptr crypto : HomomorphicCrypto shared_ptr ;
*
* Return :
* - double : Duration in seconds.
**/
double PIROptimizer::getQueryElemGenCost(unsigned int d, crypto_ptr crypto)
{
double start, stop;
double elapsed_time = 0;
unsigned int query_elts_nbr = 0;
PIRParameters pirParams;
pirParams.d = d;
pirParams.alpha = 1;
for (unsigned int i = 0 ; i < d; i++)
pirParams.n[i] = 1;
// Needed for calls to getAbsorptionBitsize
crypto->setandgetAbsBitPerCiphertext(1); // Set internally best absorption possible
PIRQueryGenerator_internal queryGenerator(pirParams, *crypto);
queryGenerator.setChosenElement(1);
do{
for (unsigned int i = 0 ; i < d; i++) pirParams.n[i] *= 2;
start = omp_get_wtime();
queryGenerator.generateQuery();
stop = omp_get_wtime();
queryGenerator.cleanQueryBuffer();
}while((elapsed_time = (stop - start)) <= 0.5);
for(unsigned int i = 0 ; i < d ; i++) query_elts_nbr += pirParams.n[i];
double result = elapsed_time / query_elts_nbr;
return result;
}
const PIRParameters& PIROptimizer::getParameters()
{
return pirParameters;
}
double PIROptimizer::getAbs1PlaintextTime(HomomorphicCrypto* crypto_system)
{
bool shortversion = true;
return abs_cache[currentPublicParams->getSerializedParams(shortversion)];
}
double PIROptimizer::getPrecompute1PlaintextTime(HomomorphicCrypto* crypto_system)
{
bool shortversion = true;
return precompute_cache[currentPublicParams->getSerializedParams(shortversion)];
}
/**
* Return natural aggregation value.
**/
unsigned int PIROptimizer::getMinAlpha()
{
double r = ceil(Tp(0) / static_cast(fixedVars.l));
r = (r > fixedVars.n) ? fixedVars.n : r;
unsigned ret = (r < 1.0) ? 1 : unsigned(r);
/*No limit for agregation*/
if (fixedVars.alphaMax == 0)
return ret;
else
return min(ret, fixedVars.alphaMax);
}
unsigned int PIROptimizer::getMaxAlpha()
{
return ((fixedVars.alphaMax == 0) || (fixedVars.n < fixedVars.alphaMax)) ? fixedVars.n : fixedVars.alphaMax;
}
/**
* Given the fixed vars and the choices done in the optimize loop, estimate costs.
* If total cost is better than the previus optimum, replace it.
* Return total cost of the PIR retrieval (given the fitness method in vars).
**/
double PIROptimizer::computeOptimVars(unsigned int alpha, unsigned int d, OptimVars& vars)
{
if (alpha == 0) alpha = 1;
unsigned int dn[fixedVars.dMax];
// Compute dimension sizes given the number of files, aggregation, and recursion levels
getDimSize(fixedVars.n, alpha, d, dn);
//Save alpha and d values.
vars.setAlpha(alpha);
vars.setDim(d);
crypto->setandgetAbsBitPerCiphertext(dn[0]);
// Get costs for query generation, emission, reply generation, reception, and decryption
vars.setGenQ(getGenQueryCost(d, dn));
vars.setSendQ(getSendCost(fixedVars.Tupc, fixedVars.Tdos, nbc, d, dn));
vars.setGenR(getReplyGenCost(alpha, d, dn));
vars.setSendR(getReceiveCost(alpha, fixedVars.Tups, fixedVars.Tdoc, nbc, d));
vars.setDecR(getDecryptCost(alpha, d));
// Decide whether this test is better than the optimum. If so replace it.
analyseResult(alpha, d, vars);
// Return total cost of the PIR retrieval (given the fitness method in vars)
return vars.getValue();
}
void PIROptimizer::processResults(unsigned int i)
{
if (silent == false) showBestResults(i);
writeTestBestResult(i);
}
/**
* Test current result to get the optimum.
**/
void PIROptimizer::analyseResult(unsigned int alpha, unsigned int d, OptimVars& vars)
{
bool shortversion = true;
// < operator is defined in OptimVars object, compares total cost value
if(vars < optimum)
{
// If vars is better than the current optimum redefine it
optimum = vars;
// Get a final version of the parameters with the absorption size
optimum.crypto_params = crypto->getSerializedCryptoParams(!shortversion);
#ifdef DEBUG
cout << "PIROptimizer: New optimum cryptoparams " << optimum.crypto_params << endl;
#endif
}
}
void PIROptimizer::getFixedVarsFromServer(FixedVars& fixed_vars, boost::asio::ip::tcp::socket& s)
{
try{
/*Send command*/
size_t cmd = DATA;
write(s, boost::asio::buffer(&cmd, sizeof(cmd)));
/*Get number of files*/
read(s, boost::asio::buffer(&fixed_vars.n, sizeof(fixed_vars.n)));
cout << "Optimizer: Number of files is " << fixed_vars.n << endl;
/*Get max file size*/
read(s, boost::asio::buffer(&fixed_vars.l, sizeof(fixed_vars.l)));
cout << "Optimizer: File bytesize is " << fixed_vars.l << endl;
// fixed_vars.l should be in bits
fixed_vars.l*=8;
}catch(const std::exception& ex)
{
std::cerr << "Catched exception in " << __FUNCTION__ << ": " << ex.what() << std::endl;
}
}
/**
* Print the best result.
**/
void PIROptimizer::showBestResults(unsigned int i)
{
writeBigMessage(" RESULTS exp " + to_string(i+1) + " ");
cout << "\tparams : " << optimum.crypto_params << endl;
cout << "\td : " << optimum.getDim() << endl;
cout << "\talpha : " << optimum.getAlpha() << endl;
cout << "\tGenQ : " << optimum.getGenQ() << " sec" << endl;
cout << "\tSenQ : " << optimum.getSendQ() << " sec" << endl;
cout << "\tGenR : " << optimum.getGenR() << " sec" << endl;
cout << "\tSendR : " << optimum.getSendR() << " sec" << endl;
cout << "\tDecR : " << optimum.getDecR() << " sec" << endl;
cout << "\tTotal Time : "<< optimum.getValue() << " sec" << endl;
cout << "\t--Fixed Vars-- " << endl;
cout << "\tl : " << fixedVars.l << " bits"<< endl;
cout << "\tn : " << fixedVars.n << endl;
cout << "\tTupc/dos : "<< fixedVars.Tupc << " b/s" << endl;
cout << "\tTdoc/ups : "<< fixedVars.Tdoc << " b/s" << endl;
writeBigMessage("##############");
}
void PIROptimizer::getPreComputedData(HomomorphicCrypto* crypto)
{
std::string fdec_path(OptimService::folderName + OptimService::fileName + crypto->toString()
+ OptimService::decFileExtension);
std::string fenc_path(OptimService::folderName + OptimService::fileName + crypto->toString()
+ OptimService::encFileExtension);
decrypt_cache.clear();
encrypt_cache.clear();
set crypto_params_set;
crypto->getAllCryptoParams(crypto_params_set);
//if (OptimService::verifyOptimData(crypto_params_set, fdec_path, fenc_path))
if (OptimService::fileOutdated(crypto->toString(), OptimService::encFileExtension) ||
OptimService::fileOutdated(crypto->toString(), OptimService::decFileExtension) )
{
cout << "PIROptimizer:: Computing cache data for " << crypto->toString() << ", this can take a while..." << endl;
computeOptimData(crypto_params_set);
cout << "PIROptimizer: Finished computing cache data for "<< crypto->toString() << endl;
}
else
{
OptimService::readOptimData(decrypt_cache, fdec_path);
OptimService::readOptimData(encrypt_cache, fenc_path);
}
}
void PIROptimizer::computeOptimData(set &crypto_params_set )
{
unsigned int i = 1;
unsigned int crypto_params_nbr = crypto_params_set.size();
string optim_data2write;
// Generate the encryption performance cache
std::string file_path(OptimService::folderName + OptimService::fileName + crypto->toString());
for (auto crypto_param : crypto_params_set)
{
cout << "PIROptimizer: Encrypt cache generation for " << crypto_param << " " << i++
<< "/" << crypto_params_nbr << "." << flush;
crypto->setNewParameters(crypto_param);
cout << "." << endl;
encrypt_cache[crypto_param] = getQueryElemGenCost(1, crypto);
std::ostringstream out;
out << std::setprecision(kPrecision) << encrypt_cache[crypto_param];
optim_data2write += crypto_param + " " + out.str() + "\n";
}
if(OptimService::writeOptimDataBuffer(optim_data2write, file_path+OptimService::encFileExtension))
{
std::cout << "PIROptimizer: Error when writing optimization data, aborting." << std::endl;
exit(1);
}
optim_data2write = "";
i=1;
// Generate the decryption performance cache
for (auto crypto_param : crypto_params_set)
{
cout << "PIROptimizer: Decrypt cache generation for " << crypto_param << " " << i++
<< "/" << crypto_params_nbr << "." << flush;
crypto->setNewParameters(crypto_param);
cout << "." << endl;
decrypt_cache[crypto_param] = getDecCost(1, crypto);
std::ostringstream out;
out << std::setprecision(kPrecision) << decrypt_cache[crypto_param];
optim_data2write += crypto_param + " " + out.str() + "\n";
}
if(OptimService::writeOptimDataBuffer(optim_data2write, file_path+OptimService::decFileExtension))
{
std::cout << "PIROptimizer: Error when writing optimization data, aborting." << std::endl;
exit(1);
}
}
/**
* Writes values of the best result.
**/
void PIROptimizer::writeTestBestResult(unsigned int i)
{
OptimService::writeMessage(i, "### BEST RESULT ###");
OptimService::writeFootFile(i);
OptimService::writeConfigFile(getMinAlpha(), optimum.getAlpha(), optimum.getDim(), i);
}
/**
* Disconect to the sever's optimizer.
**/
void PIROptimizer::disconnect()
{
try
{
size_t cmd = EXIT;
s.send(boost::asio::buffer(&cmd, sizeof(size_t)));// exit function
s.close();
}catch (const std::exception& ex)
{}
}
void PIROptimizer::connect()
{
try
{
tcp::endpoint endpoint(boost::asio::ip::address::from_string(serv_ip), server_optimport);
s.connect(endpoint);
}catch(const std::exception& ex )
{
}
}
/**
* Print "big" message on the terminal.
**/
void PIROptimizer::writeBigMessage(string msg)
{
std::transform(msg.begin(), msg.end(), msg.begin(), ::toupper);
cout << "########" << msg << "########" << endl;
}
PIROptimizer::~PIROptimizer()
{
}
/**
* Do a network speed test with the server.
* Params :
* - int port : server port
**/
void PIROptimizer::getNetworkInfos(FixedVars& fixedVars, boost::asio::ip::tcp::socket& s)
{
double start = 0, end=0, loop = 1;
char *msg = (char *) malloc(MEGA_BYTE+4);
unsigned int read_size = 0;
memset(msg, 1, MEGA_BYTE);
size_t cmd = SPEED;
/*Send command*/
write(s, boost::asio::buffer(&cmd, sizeof(size_t)));
// Do upload test
start = omp_get_wtime();
for (int i = 0 ; i < loop ; i++) write(s, boost::asio::buffer(msg, MEGA_BYTE));
end = omp_get_wtime();
// Ignore it if value was forced
if (fixedVars.Tupc != 0)
{
cout << "Optimizer: Upload speed value forced, dropping network test result" << std::endl;
}
else
{
fixedVars.Tupc = (loop /(end - start)) * 8 * MEGA_BYTE;
fixedVars.Tdos = fixedVars.Tupc;
cout << "Optimizer: Upload speed test gives " << fixedVars.Tupc << " bits/s" << endl;
}
// Do download tests
start = omp_get_wtime();
for (int i = 0 ; i < loop ; i++) read(s, boost::asio::buffer(msg, MEGA_BYTE));
end = omp_get_wtime();
if (fixedVars.Tdoc != 0)
{
cout << "Optimizer: Download speed value forced, dropping network test result" << endl;
}
else
{
fixedVars.Tdoc = loop / (end - start) * 8 * MEGA_BYTE;
fixedVars.Tups = fixedVars.Tdoc;
cout << "Optimizer: Download speed test gives " << fixedVars.Tdoc << " bits/s" << endl;
}
free(msg);
}
void PIROptimizer::sendExitCommand(boost::asio::ip::tcp::socket& s)
{
try{
size_t cmd = EXIT;
write(s, boost::asio::buffer(&cmd, sizeof(cmd)));
}catch(std::exception const& ex)
{
cerr << "Error when sending EXIT command to the server: " << ex.what() << endl;
}
}