Implemented passive operating mode.

This commit is contained in:
ravinsp
2019-11-11 11:59:17 +05:30
parent ef46666fd2
commit 403f2e1b21
9 changed files with 141 additions and 104 deletions

View File

@@ -14,15 +14,18 @@ contract_ctx ctx;
// Global configuration struct exposed to the application.
contract_config cfg;
const static char *MODE_PASSIVE = "passive";
const static char *MODE_ACTIVE = "active";
// provide a safe std::string overload for realpath
std::string realpath(std::string path) {
std::string realpath(std::string path)
{
std::array<char, PATH_MAX> buffer;
::realpath(path.c_str(), buffer.data());
buffer[PATH_MAX] = '\0';
return buffer.data();
}
/**
* Loads and initializes the contract config for execution. Must be called once during application startup.
* @return 0 for success. -1 for failure.
@@ -37,13 +40,16 @@ int init()
if (validate_contract_dir_paths() != 0 || load_config() != 0 || validate_config() != 0)
return -1;
// Append self peer to peer list.
const std::string portstr = std::to_string(cfg.peerport);
const std::string peerid = "0.0.0.0:" + portstr;
cfg.peers.emplace(std::move(peerid), std::make_pair("0.0.0.0", portstr));
if (cfg.mode == OPERATING_MODE::ACTIVE)
{
// Append self peer to peer list.
const std::string portstr = std::to_string(cfg.peerport);
const std::string peerid = "0.0.0.0:" + portstr;
cfg.peers.emplace(std::move(peerid), std::make_pair("0.0.0.0", portstr));
// Append self pubkey to unl list.
cfg.unl.emplace(cfg.pubkey);
// Append self pubkey to unl list.
cfg.unl.emplace(cfg.pubkey);
}
return 0;
}
@@ -65,7 +71,7 @@ int rekey()
if (save_config() != 0)
return -1;
std::cout << "New signing keys generated at " << ctx.configFile << std::endl;
std::cout << "New signing keys generated at " << ctx.configfile << std::endl;
return 0;
}
@@ -77,16 +83,16 @@ int rekey()
*/
int create_contract()
{
if (boost::filesystem::exists(ctx.contractDir))
if (boost::filesystem::exists(ctx.contractdir))
{
std::cout << "Contract dir already exists. Cannot create contract at the same location.\n";
return -1;
}
boost::filesystem::create_directories(ctx.configDir);
boost::filesystem::create_directories(ctx.histDir);
boost::filesystem::create_directories(ctx.stateDir);
boost::filesystem::create_directories(ctx.stateMapDir);
boost::filesystem::create_directories(ctx.configdir);
boost::filesystem::create_directories(ctx.histdir);
boost::filesystem::create_directories(ctx.statedir);
boost::filesystem::create_directories(ctx.statemapdir);
//Create config file with default settings.
@@ -96,6 +102,7 @@ int create_contract()
if (binpair_to_hex() != 0)
return -1;
cfg.mode = OPERATING_MODE::ACTIVE;
cfg.listenip = "0.0.0.0";
cfg.peerport = 22860;
cfg.roundtime = 1000;
@@ -107,14 +114,14 @@ int create_contract()
cfg.loglevel = "warn";
#endif
cfg.loggers.emplace("console");
cfg.binary = "<your contract binary here>";
//Save the default settings into the config file.
if (save_config() != 0)
return -1;
std::cout << "Contract directory created at " << ctx.contractDir << std::endl;
std::cout << "Contract directory created at " << ctx.contractdir << std::endl;
return 0;
}
@@ -125,24 +132,25 @@ int create_contract()
*/
void set_contract_dir_paths(std::string basedir)
{
if (basedir == "") {
if (basedir == "")
{
// this code branch will never execute the way main is currently coded, but it might change in future
std::cerr << "a contract directory must be specified\n";
exit(1);
}
// resolving the path through realpath will remove any trailing slash if present
basedir = realpath(basedir);
ctx.contractDir = basedir;
ctx.configDir = basedir + "/cfg";
ctx.configFile = ctx.configDir + "/hp.cfg";
ctx.tlsKeyFile = ctx.configDir + "/tlskey.pem";
ctx.tlsCertFile = ctx.configDir + "/tlscert.pem";
ctx.histDir = basedir + "/hist";
ctx.stateDir = basedir + "/state";
ctx.stateMapDir = basedir + "/statemap";
ctx.logDir = basedir + "/log";
ctx.contractdir = basedir;
ctx.configdir = basedir + "/cfg";
ctx.configfile = ctx.configdir + "/hp.cfg";
ctx.tlskeyfile = ctx.configdir + "/tlskey.pem";
ctx.tlscertfile = ctx.configdir + "/tlscert.pem";
ctx.histdir = basedir + "/hist";
ctx.statedir = basedir + "/state";
ctx.statemapdir = basedir + "/statemap";
ctx.logdir = basedir + "/log";
}
/**
@@ -154,7 +162,7 @@ int load_config()
{
// Read the file into json document object.
std::ifstream ifs(ctx.configFile);
std::ifstream ifs(ctx.configfile);
rapidjson::IStreamWrapper isw(ifs);
rapidjson::Document d;
@@ -195,13 +203,22 @@ int load_config()
// Load up the values into the struct.
if (d["mode"] == MODE_PASSIVE)
cfg.mode = OPERATING_MODE::PASSIVE;
else if (d["mode"] == MODE_ACTIVE)
cfg.mode = OPERATING_MODE::ACTIVE;
else
{
std::cout << "Invalid mode. 'passive' or 'active' expected.\n";
return -1;
}
cfg.pubkeyhex = d["pubkeyhex"].GetString();
cfg.seckeyhex = d["seckeyhex"].GetString();
cfg.listenip = d["listenip"].GetString();
cfg.binary = realpath(d["binary"].GetString());
cfg.binargs = d["binargs"].GetString();
cfg.binary = d["binary"].GetString();
cfg.binargs = d["binargs"].GetString();
// Storing peers in unordered map keyed by the concatenated address:port and also saving address and port
// seperately to retrieve easily when handling peer connections.
@@ -277,6 +294,9 @@ int save_config()
d.SetObject();
rapidjson::Document::AllocatorType &allocator = d.GetAllocator();
d.AddMember("version", rapidjson::StringRef(util::HP_VERSION), allocator);
d.AddMember("mode", rapidjson::StringRef(cfg.mode == OPERATING_MODE::PASSIVE ? MODE_PASSIVE : MODE_ACTIVE),
allocator);
d.AddMember("pubkeyhex", rapidjson::StringRef(cfg.pubkeyhex.data()), allocator);
d.AddMember("seckeyhex", rapidjson::StringRef(cfg.seckeyhex.data()), allocator);
d.AddMember("binary", rapidjson::StringRef(cfg.binary.data()), allocator);
@@ -338,13 +358,13 @@ int save_config()
// Write the json doc to file.
std::ofstream ofs(ctx.configFile);
std::ofstream ofs(ctx.configfile);
rapidjson::OStreamWrapper osw(ofs);
rapidjson::PrettyWriter<rapidjson::OStreamWrapper> writer(osw);
if (!d.Accept(writer))
{
std::cout << "Writing to config file failed. " << ctx.configFile << std::endl;
std::cout << "Writing to config file failed. " << ctx.configfile << std::endl;
return -1;
}
ofs.close();
@@ -426,20 +446,20 @@ int validate_config()
}
// Other required fields.
bool fields_missing = false;
fields_missing |= cfg.binary.empty() && std::cout << "Missing cfg field: binary\n";
fields_missing |= cfg.listenip.empty() && std::cout << "Missing cfg field: listenip\n";
fields_missing |= cfg.peerport ==0 && std::cout << "Missing cfg field: peerport\n";
fields_missing |= cfg.roundtime == 0 && std::cout << "Missing cfg field: roundtime\n";
fields_missing |= cfg.pubport == 0 && std::cout << "Missing cfg field: pubport\n";
fields_missing |= cfg.loglevel.empty() && std::cout << "Missing cfg field: loglevel\n";
fields_missing |= cfg.loggers.empty() && std::cout << "Missing cfg field: loggers\n";
fields_missing |= cfg.binary.empty() && std::cout << "Missing cfg field: binary\n";
fields_missing |= cfg.listenip.empty() && std::cout << "Missing cfg field: listenip\n";
fields_missing |= cfg.peerport == 0 && std::cout << "Missing cfg field: peerport\n";
fields_missing |= cfg.roundtime == 0 && std::cout << "Missing cfg field: roundtime\n";
fields_missing |= cfg.pubport == 0 && std::cout << "Missing cfg field: pubport\n";
fields_missing |= cfg.loglevel.empty() && std::cout << "Missing cfg field: loglevel\n";
fields_missing |= cfg.loggers.empty() && std::cout << "Missing cfg field: loggers\n";
if (fields_missing)
{
std::cout << "Required configuration fields missing at " << ctx.configFile << std::endl;
std::cout << "Required configuration fields missing at " << ctx.configfile << std::endl;
return -1;
}
@@ -488,23 +508,23 @@ int validate_config()
int validate_contract_dir_paths()
{
const std::string paths[7] = {
ctx.contractDir,
ctx.configFile,
ctx.histDir,
ctx.stateDir,
ctx.stateMapDir,
ctx.tlsKeyFile,
ctx.tlsCertFile};
ctx.contractdir,
ctx.configfile,
ctx.histdir,
ctx.statedir,
ctx.statemapdir,
ctx.tlskeyfile,
ctx.tlscertfile};
for (const std::string &path : paths)
{
if (!boost::filesystem::exists(path))
{
if (path == ctx.tlsKeyFile || path == ctx.tlsCertFile)
if (path == ctx.tlskeyfile || path == ctx.tlscertfile)
{
std::cout << path << " does not exist. Please provide self-signed certificates. Can generate using command\n"
<< "openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout tlskey.pem -out tlscert.pem\n"
<< "and add it to " + ctx.configDir << std::endl;
<< "and add it to " + ctx.configdir << std::endl;
}
else
{
@@ -528,12 +548,13 @@ int is_schema_valid(const rapidjson::Document &d)
const char *cfg_schema =
"{"
"\"type\": \"object\","
"\"required\": [ \"version\", \"pubkeyhex\", \"seckeyhex\", \"binary\", \"binargs\", \"listenip\""
"\"required\": [ \"mode\", \"version\", \"pubkeyhex\", \"seckeyhex\", \"binary\", \"binargs\", \"listenip\""
", \"peers\", \"unl\", \"pubport\", \"peerport\", \"roundtime\""
", \"pubmaxsize\", \"pubmaxcpm\", \"pubmaxbadmpm\", \"pubmaxcons\""
", \"peermaxsize\", \"peermaxcpm\", \"peermaxdupmpm\", \"peermaxbadmpm\", \"peermaxbadsigpm\", \"peermaxcons\""
", \"loglevel\", \"loggers\" ],"
"\"properties\": {"
"\"mode\": { \"type\": \"string\" },"
"\"version\": { \"type\": \"string\" },"
"\"pubkeyhex\": { \"type\": \"string\" },"
"\"seckeyhex\": { \"type\": \"string\" },"
@@ -566,7 +587,7 @@ int is_schema_valid(const rapidjson::Document &d)
"\"loggers\": {"
"\"type\": \"array\","
"\"items\": { \"type\": \"string\" }"
"},"
"}"
"}"
"}";

View File

@@ -13,20 +13,27 @@ namespace conf
// Typedef to represent ip address and port pair.
typedef std::pair<std::string, std::string> ip_port_pair;
// The operating mode of the contract node.
enum OPERATING_MODE
{
PASSIVE = 0, // Observer mode. Only emits NUPs. Does not participate in voting.
ACTIVE = 1 // Consensus participant mode.
};
// Holds contextual information about the currently loaded contract.
struct contract_ctx
{
std::string command; // The CLI command issued to launch HotPocket
std::string command; // The CLI command issued to launch HotPocket
std::string contractDir; // Contract base directory
std::string histDir; // Contract history dir
std::string stateDir; // Contract state dir
std::string stateMapDir; // Contract state map dir (.merkel files)
std::string logDir; // Contract log dir
std::string configDir; // Contract config dir
std::string configFile; // Full path to the contract config file
std::string tlsKeyFile; // Full path to the tls secret key file
std::string tlsCertFile; // Full path to the tls certificate
std::string contractdir; // Contract base directory full path
std::string histdir; // Contract history dir full path
std::string statedir; // Contract state dir full path
std::string statemapdir; // Contract state map dir (.merkel files) full path
std::string logdir; // Contract log dir full path
std::string configdir; // Contract config dir full path
std::string configfile; // Full path to the contract config file
std::string tlskeyfile; // Full path to the tls secret key file
std::string tlscertfile; // Full path to the tls certificate
};
// Holds all the contract config values.
@@ -34,37 +41,38 @@ struct contract_config
{
// Config elements which are initialized in memory (these are not directly loaded from the config file)
std::string pubkey; // Contract public key bytes
std::string seckey; // Contract secret key bytes
std::string pubkey; // Contract public key bytes
std::string seckey; // Contract secret key bytes
// Config elements which are loaded from the config file.
std::string pubkeyhex; // Contract hex public key
std::string seckeyhex; // Contract hex secret key
std::string keytype; // Key generation algorithm used by libsodium
std::string binary; // Full path to the contract binary
std::string binargs; // CLI arguments to pass to the contract binary
std::string listenip; // The IPs to listen on for incoming connections
OPERATING_MODE mode; // Operating mode of the contract (Passive/Active).
std::string pubkeyhex; // Contract hex public key
std::string seckeyhex; // Contract hex secret key
std::string keytype; // Key generation algorithm used by libsodium
std::string binary; // Full path to the contract binary
std::string binargs; // CLI arguments to pass to the contract binary
std::string listenip; // The IPs to listen on for incoming connections
std::unordered_map<std::string, ip_port_pair> peers; // Map of peers keyed by "<ip address>:<port>" concatenated format
std::unordered_set<std::string> unl; // Unique node list (list of binary public keys)
uint16_t peerport; // Listening port for peer connections
uint16_t roundtime; // Consensus round time in ms
uint16_t pubport; // Listening port for public user connections
uint16_t peerport; // Listening port for peer connections
uint16_t roundtime; // Consensus round time in ms
uint16_t pubport; // Listening port for public user connections
uint64_t pubmaxsize; // User message max size in bytes
uint64_t pubmaxcpm; // User message rate (characters(bytes) per minute)
uint64_t pubmaxbadmpm; // User bad messages per minute
uint16_t pubmaxcons; // Max inbound user connections
uint64_t pubmaxsize; // User message max size in bytes
uint64_t pubmaxcpm; // User message rate (characters(bytes) per minute)
uint64_t pubmaxbadmpm; // User bad messages per minute
uint16_t pubmaxcons; // Max inbound user connections
uint64_t peermaxsize; // Peer message max size in bytes
uint64_t peermaxcpm; // Peer message rate (characters(bytes) per minute)
uint64_t peermaxdupmpm; // Peer max duplicate messages per minute
uint64_t peermaxbadmpm; // Peer bad messages per minute
uint64_t peermaxbadsigpm; // Peer bad signatures per minute
uint16_t peermaxcons; // Max inbound peer connections
uint64_t peermaxsize; // Peer message max size in bytes
uint64_t peermaxcpm; // Peer message rate (characters(bytes) per minute)
uint64_t peermaxdupmpm; // Peer max duplicate messages per minute
uint64_t peermaxbadmpm; // Peer bad messages per minute
uint64_t peermaxbadsigpm; // Peer bad signatures per minute
uint16_t peermaxcons; // Max inbound peer connections
std::string loglevel; // Log severity level (debug, info, warn, error)
std::unordered_set<std::string> loggers; // List of enabled loggers (console, file)
std::string loglevel; // Log severity level (debug, info, warn, error)
std::unordered_set<std::string> loggers; // List of enabled loggers (console, file)
};
// Global contract context struct exposed to the application.

View File

@@ -357,6 +357,10 @@ p2p::proposal create_stage123_proposal(vote_counter &votes)
*/
void broadcast_proposal(const p2p::proposal &p)
{
// In passive mode, we do not send out any propopsals.
if (conf::cfg.mode == conf::OPERATING_MODE::PASSIVE)
return;
p2p::peer_outbound_message msg(std::make_shared<flatbuffers::FlatBufferBuilder>(1024));
p2pmsg::create_msg_from_proposal(msg.builder(), p);
p2p::broadcast_message(msg);
@@ -382,7 +386,7 @@ void check_majority_stage(bool &is_desync, bool &should_reset, uint8_t &majority
// todo:vote for lcl checking condtion
}
majority_stage = -1;
majority_stage = 0;
is_desync = false;
int32_t highest_votes = 0;

View File

@@ -28,8 +28,8 @@ std::string save_ledger(const p2p::proposal &proposal, const uint64_t led_seq_no
//file name -> [ledger sequnce numer]-[lcl hex]
std::string path;
const std::string seq_no = std::to_string(led_seq_no);
path.reserve(conf::ctx.histDir.size() + lcl_hash.size() + seq_no.size() + 6);
path.append(conf::ctx.histDir);
path.reserve(conf::ctx.histdir.size() + lcl_hash.size() + seq_no.size() + 6);
path.append(conf::ctx.histdir);
path.append("/");
path.append(seq_no);
path.append("-");
@@ -54,18 +54,18 @@ ledger_history load_ledger()
//Get all records at lcl history direcory and find the last closed ledger.
std::string latest_file_name;
size_t latest_pos = 0;
for (const auto &entry : boost::filesystem::directory_iterator(conf::ctx.histDir))
for (const auto &entry : boost::filesystem::directory_iterator(conf::ctx.histdir))
{
const boost::filesystem::path file_path = entry.path();
const std::string file_name = entry.path().filename().string();
if (boost::filesystem::is_directory(file_path))
{
LOG_ERR << "Found directory " << file_name << " in " << conf::ctx.histDir << ". There should be no folders in this directory";
LOG_ERR << "Found directory " << file_name << " in " << conf::ctx.histdir << ". There should be no folders in this directory";
}
else if (file_path.extension() != ".lcl")
{
LOG_ERR << "Found invalid file extension: " << file_path.extension() << " for lcl file " << file_name << " in " << conf::ctx.histDir;
LOG_ERR << "Found invalid file extension: " << file_path.extension() << " for lcl file " << file_name << " in " << conf::ctx.histdir;
}
else
{

View File

@@ -46,10 +46,10 @@ int update_file_blockmap(const std::string &filepath, const std::set<uint32_t> &
{
// .merkel file path will be corresponding path in "statemap" directory.
std::string merkle_fn;
const size_t relative_path_len = filepath.length() - conf::ctx.stateDir.length();
merkle_fn.reserve(conf::ctx.stateMapDir.length() + relative_path_len + 7);
merkle_fn.append(conf::ctx.stateMapDir);
merkle_fn.append(filepath.substr(conf::ctx.stateDir.length(), relative_path_len));
const size_t relative_path_len = filepath.length() - conf::ctx.statedir.length();
merkle_fn.reserve(conf::ctx.statemapdir.length() + relative_path_len + 7);
merkle_fn.append(conf::ctx.statemapdir);
merkle_fn.append(filepath.substr(conf::ctx.statedir.length(), relative_path_len));
merkle_fn.append(MERKLE_EXTENSION);
// To benefit from hint mode, the .merkle file must already exist. If not we simply disable hint mode

View File

@@ -57,8 +57,8 @@ void init()
if (conf::cfg.loggers.count("file") == 1)
{
logging::add_file_log(
keywords::target = conf::ctx.logDir, // Log file directory.
keywords::file_name = conf::ctx.logDir + "/hp_%N.log", // File name pattern "hp_1.log".
keywords::target = conf::ctx.logdir, // Log file directory.
keywords::file_name = conf::ctx.logdir + "/hp_%N.log", // File name pattern "hp_1.log".
keywords::rotation_size = 10 * 1024 * 1024, // Rotate files every 10 MB.
keywords::max_size = 500 * 1024 * 1024, // Do not exceed 500 MB total logs.
keywords::filter = (a_severity >= severity),

View File

@@ -157,11 +157,15 @@ int main(int argc, char **argv)
if (conf::init() != 0)
return -1;
// Set HP process cwd to the contract directory.
chdir(conf::ctx.contractDir.c_str());
// Set HP process cwd to the contract directory. This will make both HP and contract process
// both have the same cwd.
chdir(conf::ctx.contractdir.c_str());
hplog::init();
LOG_INFO << "Operating mode: "
<< (conf::cfg.mode == conf::OPERATING_MODE::PASSIVE ? "Passive" : "Active");
if (p2p::init() != 0 || usr::init() != 0 || cons::init() != 0)
return -1;

View File

@@ -139,7 +139,7 @@ int ptrace_capture(const pid_t child, contract_fblockmap_t &updated_blocks)
realpath(filenameptr, buf);
// We ignore anything outside the state dir.
if (strncmp (buf, conf::ctx.stateDir.c_str(), conf::ctx.stateDir.size()) != 0)
if (strncmp (buf, conf::ctx.statedir.c_str(), conf::ctx.statedir.size()) != 0)
continue;
fd_map[fd] = {std::string(buf), 0};

View File

@@ -58,11 +58,11 @@ void socket_server<T>::run()
boost::asio::ssl::context::no_sslv3);
//Providing the certification file for ssl context
ctx.use_certificate_chain_file(conf::ctx.tlsCertFile);
ctx.use_certificate_chain_file(conf::ctx.tlscertfile);
// Providing key file for the ssl context
ctx.use_private_key_file(
conf::ctx.tlsKeyFile,
conf::ctx.tlskeyfile,
boost::asio::ssl::context::pem);
// Start accepting a connection