src/application/impl/app_config_impl.cpp¶
Namespaces¶
| Name |
|---|
| sgns |
| sgns::application |
Source code¶
#include "application/impl/app_config_impl.hpp"
#include <rapidjson/document.h>
#include <rapidjson/filereadstream.h>
#include <boost/program_options.hpp>
#include <iostream>
namespace {
template <typename T, typename Func>
inline void find_argument(boost::program_options::variables_map &vm,
char const *name,
Func &&f) {
assert(nullptr != name);
auto it = vm.find(name);
if ( it != vm.end() )
{
std::forward<Func>( f )( it->second.as<T>() );
}
}
const std::string def_rpc_http_host = "0.0.0.0";
const std::string def_rpc_ws_host = "0.0.0.0";
const uint16_t def_rpc_http_port = 40363;
const uint16_t def_rpc_ws_port = 40364;
const uint16_t def_p2p_port = 30363;
const int def_verbosity = 2;
const bool def_is_only_finalizing = false;
} // namespace
namespace sgns::application {
AppConfigurationImpl::AppConfigurationImpl(sgns::base::Logger logger)
: logger_(std::move(logger)),
rpc_http_host_(def_rpc_http_host),
rpc_ws_host_(def_rpc_ws_host),
rpc_http_port_(def_rpc_http_port),
rpc_ws_port_(def_rpc_ws_port),
p2p_port_(def_p2p_port),
verbosity_(static_cast<spdlog::level::level_enum>(def_verbosity)),
is_only_finalizing_(def_is_only_finalizing) {}
AppConfigurationImpl::FilePtr AppConfigurationImpl::open_file(
const std::string &filepath) {
assert(!filepath.empty());
return { std::fopen( filepath.c_str(), "r" ), &std::fclose };
}
bool AppConfigurationImpl::load_str(const rapidjson::Value &val,
char const *name,
std::string &target) {
auto m = val.FindMember(name);
if (val.MemberEnd() != m && m->value.IsString()) {
target.assign(m->value.GetString(), m->value.GetStringLength());
return true;
}
return false;
}
bool AppConfigurationImpl::load_bool(const rapidjson::Value &val,
char const *name,
bool &target) {
auto m = val.FindMember(name);
if (val.MemberEnd() != m && m->value.IsBool()) {
target = m->value.GetBool();
return true;
}
return false;
}
bool AppConfigurationImpl::load_u16(const rapidjson::Value &val,
char const *name,
uint16_t &target) {
auto m = val.FindMember(name);
if (val.MemberEnd() != m && m->value.IsInt()) {
const auto v = m->value.GetInt();
const auto in_range = (v & ~std::numeric_limits<uint16_t>::max()) == 0;
if (in_range) {
target = static_cast<uint16_t>(v);
return true;
}
}
return false;
}
void AppConfigurationImpl::parse_general_segment(rapidjson::Value &val) {
uint16_t v{};
if (load_u16(val, "verbosity", v) && v <= SPDLOG_LEVEL_OFF)
verbosity_ = static_cast<spdlog::level::level_enum>(v);
}
void AppConfigurationImpl::parse_blockchain_segment(rapidjson::Value &val) {
load_str(val, "genesis", genesis_path_);
}
void AppConfigurationImpl::parse_storage_segment(rapidjson::Value &val) {
load_str(val, "rocksdb", rocksdb_path_);
}
void AppConfigurationImpl::parse_authority_segment(rapidjson::Value &val) {
load_str(val, "keystore", keystore_path_);
}
void AppConfigurationImpl::parse_network_segment(rapidjson::Value &val) {
load_u16(val, "p2p_port", p2p_port_);
load_str(val, "rpc_http_host", rpc_http_host_);
load_u16(val, "rpc_http_port", rpc_http_port_);
load_str(val, "rpc_ws_host", rpc_ws_host_);
load_u16(val, "rpc_ws_port", rpc_ws_port_);
}
void AppConfigurationImpl::parse_additional_segment(rapidjson::Value &val) {
load_bool(val, "single_finalizing_node", is_only_finalizing_);
}
void AppConfigurationImpl::validate_config(
AppConfiguration::LoadScheme scheme) {
if (genesis_path_.empty()) {
logger_->error("Node configuration must contain 'genesis' option.");
exit(EXIT_FAILURE);
}
if (rocksdb_path_.empty()) {
logger_->error("Node configuration must contain 'rocksdb_path' option.");
exit(EXIT_FAILURE);
}
if (p2p_port_ == 0) {
logger_->error("p2p port is 0.");
exit(EXIT_FAILURE);
}
if (rpc_ws_port_ == 0) {
logger_->error("RPC ws port is 0.");
exit(EXIT_FAILURE);
}
if (rpc_http_port_ == 0) {
logger_->error("RPC http port is 0.");
exit(EXIT_FAILURE);
}
const auto need_keystore =
(AppConfiguration::LoadScheme::kBlockProducing == scheme)
|| (AppConfiguration::LoadScheme::kValidating == scheme);
if (need_keystore && keystore_path_.empty()) {
logger_->error("Node configuration must contain 'keystore_path' option.");
exit(EXIT_FAILURE);
}
}
void AppConfigurationImpl::read_config_from_file(
const std::string &filepath) {
assert(!filepath.empty());
auto file = open_file(filepath);
if (!file) {
logger_->error("Configuration file path is invalid: {}", filepath);
return;
}
using FileReadStream = rapidjson::FileReadStream;
using Document = rapidjson::Document;
std::array<char, 1024> buffer_size{};
FileReadStream input_stream(
file.get(), buffer_size.data(), buffer_size.size());
Document document;
document.ParseStream(input_stream);
if (document.HasParseError()) {
logger_->error("Configuration file {} parse failed, with error {}",
filepath,
document.GetParseError());
return;
}
for (auto &handler : handlers) {
auto it = document.FindMember(handler.segment_name);
if (document.MemberEnd() != it) {
(this->*handler.handler)(it->value);
}
}
}
boost::asio::ip::tcp::endpoint AppConfigurationImpl::get_endpoint_from(
std::string const &host, uint16_t port) {
boost::asio::ip::tcp::endpoint endpoint;
boost::system::error_code err;
endpoint.address(boost::asio::ip::address::from_string(host, err));
if (err.failed()) {
logger_->error("RPC address '{}' is invalid", host);
exit(EXIT_FAILURE);
}
endpoint.port(port);
return endpoint;
}
bool AppConfigurationImpl::initialize_from_args(
AppConfiguration::LoadScheme scheme, int argc, char **argv) {
namespace po = boost::program_options;
// clang-format off
po::options_description desc("General options");
desc.add_options()
("help,h", "show this help message")
("verbosity,v", po::value<int>(), "Log level: 0 - trace, 1 - debug, 2 - info, 3 - warn, 4 - error, 5 - crit, 6 - no log")
("config_file,c", po::value<std::string>(), "Filepath to load configuration from.")
;
po::options_description blockhain_desc("Blockchain options");
blockhain_desc.add_options()
("genesis,g", po::value<std::string>(), "required, configuration file path")
;
po::options_description storage_desc("Storage options");
storage_desc.add_options()
("rocksdb,l", po::value<std::string>(), "required, rocksdb directory path")
;
po::options_description authority_desc("Authority options");
authority_desc.add_options()
("keystore,k", po::value<std::string>(), "required, keystore file path")
;
po::options_description network_desc("Network options");
network_desc.add_options()
("p2p_port,p", po::value<uint16_t>(), "port for peer to peer interactions")
("rpc_http_host", po::value<std::string>(), "address for RPC over HTTP")
("rpc_http_port", po::value<uint16_t>(), "port for RPC over HTTP")
("rpc_ws_host", po::value<std::string>(), "address for RPC over Websocket protocol")
("rpc_ws_port", po::value<uint16_t>(), "port for RPC over Websocket protocol")
;
po::options_description additional_desc("Additional options");
additional_desc.add_options()
("single_finalizing_node,f", "if this is the only finalizing node")
;
// clang-format on
po::variables_map vm;
po::parsed_options parsed = po::command_line_parser(argc, argv)
.options(desc)
.allow_unregistered()
.run();
po::store(parsed, vm);
po::notify(vm);
desc.add(blockhain_desc)
.add(storage_desc)
.add(authority_desc)
.add(network_desc)
.add(additional_desc);
if (vm.count("help") > 0) {
std::cout << desc << std::endl;
return false;
}
try {
po::store(po::parse_command_line(argc, argv, desc), vm);
po::store(parsed, vm);
po::notify(vm);
} catch (std::exception &e) {
std::cerr << "Error: " << e.what() << '\n'
<< "Try run with option '--help' for more information"
<< std::endl;
return false;
}
find_argument<std::string>(vm, "config_file", [&](std::string const &path) {
read_config_from_file(path);
});
if ( vm.end() != vm.find( "single_finalizing_node" ) )
{
is_only_finalizing_ = true;
}
find_argument<std::string>(
vm, "genesis", [&](std::string const &val) { genesis_path_ = val; });
find_argument<std::string>(
vm, "rocksdb", [&](std::string const &val) { rocksdb_path_ = val; });
find_argument<std::string>(
vm, "keystore", [&](std::string const &val) { keystore_path_ = val; });
find_argument<uint16_t>(
vm, "p2p_port", [&](uint16_t val) { p2p_port_ = val; });
find_argument<int32_t>( vm, "verbosity",
[&]( int32_t val )
{
if ( val >= SPDLOG_LEVEL_TRACE && val <= SPDLOG_LEVEL_OFF )
{
verbosity_ = static_cast<spdlog::level::level_enum>( val );
}
} );
find_argument<std::string>(
vm, "rpc_http_host", [&](std::string const &val) {
rpc_http_host_ = val;
});
find_argument<std::string>(
vm, "rpc_ws_host", [&](std::string const &val) { rpc_ws_host_ = val; });
find_argument<uint16_t>(
vm, "rpc_http_port", [&](uint16_t val) { rpc_http_port_ = val; });
find_argument<uint16_t>(
vm, "rpc_ws_port", [&](uint16_t val) { rpc_ws_port_ = val; });
rpc_http_endpoint_ = get_endpoint_from(rpc_http_host_, rpc_http_port_);
rpc_ws_endpoint_ = get_endpoint_from(rpc_ws_host_, rpc_ws_port_);
validate_config(scheme);
return true;
}
} // namespace sgns::application
Updated on 2026-03-04 at 13:10:44 -0800