Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Configuration and environment variable improvements #4613

Merged
merged 13 commits into from
May 13, 2024
143 changes: 82 additions & 61 deletions nano/lib/config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,19 @@ template <typename ElemT>
struct HexTo
{
ElemT value;

HexTo () = default;

HexTo (ElemT val) :
value{ val }
{
}

operator ElemT () const
{
return value;
}

friend std::istream & operator>> (std::istream & in, HexTo & out)
{
in >> std::hex >> out.value;
Expand Down Expand Up @@ -47,9 +56,9 @@ nano::work_thresholds const nano::work_thresholds::publish_dev (
);

nano::work_thresholds const nano::work_thresholds::publish_test ( // defaults to live network levels
get_env_threshold_or_default ("NANO_TEST_EPOCH_1", 0xffffffc000000000),
get_env_threshold_or_default ("NANO_TEST_EPOCH_2", 0xfffffff800000000), // 8x higher than epoch_1
get_env_threshold_or_default ("NANO_TEST_EPOCH_2_RECV", 0xfffffe0000000000) // 8x lower than epoch_1
nano::env::get<HexTo<uint64_t>> ("NANO_TEST_EPOCH_1").value_or (0xffffffc000000000),
nano::env::get<HexTo<uint64_t>> ("NANO_TEST_EPOCH_2").value_or (0xfffffff800000000), // 8x higher than epoch_1
nano::env::get<HexTo<uint64_t>> ("NANO_TEST_EPOCH_2_RECV").value_or (0xfffffe0000000000) // 8x lower than epoch_1
);

uint64_t nano::work_thresholds::threshold_entry (nano::work_version const version_a, nano::block_type const type_a) const
Expand Down Expand Up @@ -232,41 +241,6 @@ uint8_t get_pre_release_node_version ()
return boost::numeric_cast<uint8_t> (boost::lexical_cast<int> (NANO_PRE_RELEASE_VERSION_STRING));
}

uint64_t get_env_threshold_or_default (char const * variable_name, uint64_t const default_value)
{
auto * value = getenv (variable_name);
return value ? boost::lexical_cast<HexTo<uint64_t>> (value) : default_value;
}

uint16_t test_node_port ()
{
auto test_env = nano::get_env_or_default ("NANO_TEST_NODE_PORT", "17075");
return boost::lexical_cast<uint16_t> (test_env);
}
uint16_t test_rpc_port ()
{
auto test_env = nano::get_env_or_default ("NANO_TEST_RPC_PORT", "17076");
return boost::lexical_cast<uint16_t> (test_env);
}
uint16_t test_ipc_port ()
{
auto test_env = nano::get_env_or_default ("NANO_TEST_IPC_PORT", "17077");
return boost::lexical_cast<uint16_t> (test_env);
}
uint16_t test_websocket_port ()
{
auto test_env = nano::get_env_or_default ("NANO_TEST_WEBSOCKET_PORT", "17078");
return boost::lexical_cast<uint16_t> (test_env);
}

std::array<uint8_t, 2> test_magic_number ()
{
auto test_env = get_env_or_default ("NANO_TEST_MAGIC_NUMBER", "RX");
std::array<uint8_t, 2> ret;
std::copy (test_env.begin (), test_env.end (), ret.data ());
return ret;
}

void force_nano_dev_network ()
{
nano::network_constants::set_active_network (nano::networks::nano_dev_network);
Expand All @@ -287,11 +261,6 @@ bool slow_instrumentation ()
return is_tsan_build () || nano::running_within_valgrind ();
}

bool is_sanitizer_build ()
{
return is_asan_build () || is_tsan_build ();
}

std::string get_node_toml_config_path (std::filesystem::path const & data_path)
{
return (data_path / "config-node.toml").string ();
Expand All @@ -316,37 +285,89 @@ std::string get_tls_toml_config_path (std::filesystem::path const & data_path)
{
return (data_path / "config-tls.toml").string ();
}
} // namespace nano
}

std::string nano::get_env_or_default (char const * variable_name, std::string default_value)
uint16_t nano::test_node_port ()
{
auto value = nano::get_env (variable_name);
return value ? *value : default_value;
static auto const test_env = [] () -> std::optional<uint16_t> {
if (auto value = nano::env::get<uint16_t> ("NANO_TEST_NODE_PORT"))
{
std::cerr << "Node port overridden by NANO_TEST_NODE_PORT environment variable: " << *value << std::endl;
return *value;
}
return std::nullopt;
}();
return test_env.value_or (17075);
}

int nano::get_env_int_or_default (const char * variable_name, const int default_value)
uint16_t nano::test_rpc_port ()
{
auto value = nano::get_env (variable_name);
if (value)
{
try
static auto const test_env = [] () -> std::optional<uint16_t> {
if (auto value = nano::env::get<uint16_t> ("NANO_TEST_RPC_PORT"))
{
return boost::lexical_cast<int> (*value);
std::cerr << "RPC port overridden by NANO_TEST_RPC_PORT environment variable: " << *value << std::endl;
return *value;
}
catch (...)
return std::nullopt;
}();
return test_env.value_or (17076);
}

uint16_t nano::test_ipc_port ()
{
static auto const test_env = [] () -> std::optional<uint16_t> {
if (auto value = nano::env::get<uint16_t> ("NANO_TEST_IPC_PORT"))
{
// It is unexpected that this exception will be caught, log to cerr the reason.
std::cerr << boost::str (boost::format ("Error parsing environment variable: %1% value: %2%") % variable_name % *value);
throw;
std::cerr << "IPC port overridden by NANO_TEST_IPC_PORT environment variable: " << *value << std::endl;
return *value;
}
}
return default_value;
return std::nullopt;
}();
return test_env.value_or (17077);
}

uint16_t nano::test_websocket_port ()
{
static auto const test_env = [] () -> std::optional<uint16_t> {
if (auto value = nano::env::get<uint16_t> ("NANO_TEST_WEBSOCKET_PORT"))
{
std::cerr << "Websocket port overridden by NANO_TEST_WEBSOCKET_PORT environment variable: " << *value << std::endl;
return *value;
}
return std::nullopt;
}();
return test_env.value_or (17078);
}

uint32_t nano::test_scan_wallet_reps_delay ()
{
auto test_env = nano::get_env_or_default ("NANO_TEST_WALLET_SCAN_REPS_DELAY", "900000"); // 15 minutes by default
return boost::lexical_cast<uint32_t> (test_env);
static auto const test_env = [] () -> std::optional<uint32_t> {
if (auto value = nano::env::get<uint32_t> ("NANO_TEST_WALLET_SCAN_REPS_DELAY"))
{
std::cerr << "Wallet scan interval overridden by NANO_TEST_WALLET_SCAN_REPS_DELAY environment variable: " << *value << std::endl;
return *value;
}
return std::nullopt;
}();
return test_env.value_or (900000); // 15 minutes default
}

std::array<uint8_t, 2> nano::test_magic_number ()
{
static auto const test_env = [] () -> std::optional<std::string> {
if (auto value = nano::env::get<std::string> ("NANO_TEST_MAGIC_NUMBER"))
{
std::cerr << "Magic number overridden by NANO_TEST_MAGIC_NUMBER environment variable: " << *value << std::endl;
return *value;
}
return std::nullopt;
}();

auto value = test_env.value_or ("RX");
release_assert (value.size () == 2);
std::array<uint8_t, 2> ret{};
std::copy (value.begin (), value.end (), ret.data ());
return ret;
}

std::string_view nano::to_string (nano::networks network)
Expand Down
63 changes: 19 additions & 44 deletions nano/lib/config.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,55 +28,49 @@ char const * const NANO_PRE_RELEASE_VERSION_STRING = xstr (PRE_RELEASE_VERSION_S

char const * const BUILD_INFO = xstr (GIT_COMMIT_HASH BOOST_COMPILER) " \"BOOST " xstr (BOOST_VERSION) "\" BUILT " xstr (__DATE__);

/*
* Sanitizer info
*/
namespace nano
{
consteval bool is_asan_build ()
{
#if defined(__has_feature)
#if __has_feature(address_sanitizer)
inline bool is_asan_build ()
{
return true;
}
#else
inline bool is_asan_build ()
{
return false;
}
#endif
// GCC builds
#elif defined(__SANITIZE_ADDRESS__)
inline bool is_asan_build ()
{
return true;
}
#else
inline bool is_asan_build ()
{
return false;
}
#endif
}

consteval bool is_tsan_build ()
{
#if defined(__has_feature)
#if __has_feature(thread_sanitizer)
inline bool is_tsan_build ()
{
return true;
}
#else
inline bool is_tsan_build ()
{
return false;
}
#endif
// GCC builds
#elif defined(__SANITIZE_THREAD__)
inline bool is_tsan_build ()
{
return true;
}
#else
inline bool is_tsan_build ()
{
return false;
}
#endif
}

/** Checks if we are running with either AddressSanitizer or ThreadSanitizer */
consteval bool is_sanitizer_build ()
{
return is_asan_build () || is_tsan_build ();
}
}

namespace nano
{
Expand All @@ -85,28 +79,12 @@ uint8_t get_minor_node_version ();
uint8_t get_patch_node_version ();
uint8_t get_pre_release_node_version ();

/*
* Environment variables
*/

/*
* Get environment variable as string or `default_value` if variable is not present
*/
std::string get_env_or_default (char const * variable_name, std::string const default_value);
/*
* Get environment variable as int or `default_value` if variable is not present
*/
int get_env_int_or_default (char const * variable_name, int const default_value);
uint64_t get_env_threshold_or_default (char const * variable_name, uint64_t const default_value);

uint16_t test_node_port ();
uint16_t test_rpc_port ();
uint16_t test_ipc_port ();
uint16_t test_websocket_port ();
std::array<uint8_t, 2> test_magic_number ();
/*
* How often to scan for representatives in local wallet, in milliseconds
*/
/// How often to scan for representatives in local wallet, in milliseconds
uint32_t test_scan_wallet_reps_delay ();

/**
Expand Down Expand Up @@ -412,9 +390,6 @@ bool memory_intensive_instrumentation ();
Returns true if running within Valgrind or with ThreadSanitizer tooling*/
bool slow_instrumentation ();

/** Checks if we are running with either AddressSanitizer or ThreadSanitizer*/
bool is_sanitizer_build ();

/** Set the active network to the dev network */
void force_nano_dev_network ();

Expand Down
7 changes: 4 additions & 3 deletions nano/lib/env.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

#include <string>

std::optional<std::string> nano::get_env (std::string_view name)
std::optional<std::string> nano::env::get (std::string_view name)
{
std::string name_str{ name };
if (auto value = std::getenv (name_str.c_str ()))
Expand All @@ -14,12 +14,13 @@ std::optional<std::string> nano::get_env (std::string_view name)
return std::nullopt;
}

std::optional<bool> nano::get_env_bool (std::string_view name)
template <>
std::optional<bool> nano::env::get (std::string_view name)
clemahieu marked this conversation as resolved.
Show resolved Hide resolved
{
std::vector<std::string> const on_values{ "1", "true", "on" };
std::vector<std::string> const off_values{ "0", "false", "off" };

if (auto value = get_env (name))
if (auto value = get (name))
{
// Using case-insensitive comparison
if (std::any_of (on_values.begin (), on_values.end (), [&value] (auto const & on) { return boost::iequals (*value, on); }))
Expand Down
37 changes: 32 additions & 5 deletions nano/lib/env.hpp
Original file line number Diff line number Diff line change
@@ -1,15 +1,42 @@
#pragma once

#include <boost/lexical_cast.hpp>

#include <optional>
#include <string_view>

namespace nano
namespace nano::env
{
/*
/**
* Get environment variable as a specific type or none if variable is not present.
*/
std::optional<std::string> get (std::string_view name);

/**
* Get environment variable as a specific type or none if variable is not present.
* @throws std::invalid_argument if the value cannot be converted
*/
std::optional<std::string> get_env (std::string_view name);
template <typename T>
std::optional<T> get (std::string_view name)
{
if (auto value = get (name))
{
try
{
return boost::lexical_cast<T> (*value);
}
catch (boost::bad_lexical_cast const &)
{
throw std::invalid_argument ("Invalid environment value: " + *value);
}
}
return std::nullopt;
}

// @throws std::invalid_argument if the value is not a valid boolean
std::optional<bool> get_env_bool (std::string_view name);
/**
* Specialization for boolean values.
* @throws std::invalid_argument if the value is not a valid boolean
*/
template <>
std::optional<bool> get (std::string_view name);
}
Loading
Loading