Error type and config handling (#1416)

* Error type and config handling

* Prevent splash screen from obscuring error dialog

* Remove unnecessary string conversions

* Add missing include

* msvc barks at inline constexpr (N3652)

* Remove unnecessary inline specifier

* Incorporate review feedback

* Remove superfluous inline keywords, fix size->empty

* Use any_of instead of raw loop

* Simplify file/stream handling and tests

* Missing header

* Explicit conversion operators, defaulted c'tors
This commit is contained in:
cryptocode 2019-01-14 13:25:45 +01:00 committed by GitHub
commit 282cc0ddd3
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 1420 additions and 687 deletions

View file

@ -1,5 +1,6 @@
#include <gtest/gtest.h>
#include <nano/core_test/testutil.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/node/testing.hpp>
#include <nano/node/working.hpp>
@ -428,7 +429,7 @@ TEST (logging, serialization)
logging1.work_generation_time_value = !logging1.work_generation_time_value;
logging1.log_to_cerr_value = !logging1.log_to_cerr_value;
logging1.max_size = 10;
boost::property_tree::ptree tree;
nano::jsonconfig tree;
logging1.serialize_json (tree);
nano::logging logging2;
logging2.init (path);
@ -460,7 +461,7 @@ TEST (logging, upgrade_v1_v2)
logging1.init (path1);
nano::logging logging2;
logging2.init (path2);
boost::property_tree::ptree tree;
nano::jsonconfig tree;
logging1.serialize_json (tree);
tree.erase ("version");
tree.erase ("vote");
@ -499,7 +500,7 @@ TEST (node_config, serialization)
config1.callback_port = 10;
config1.callback_target = "test";
config1.lmdb_max_dbs = 256;
boost::property_tree::ptree tree;
nano::jsonconfig tree;
config1.serialize_json (tree);
nano::logging logging2;
logging2.init (path);
@ -541,27 +542,27 @@ TEST (node_config, v1_v2_upgrade)
auto path (nano::unique_path ());
nano::logging logging1;
logging1.init (path);
boost::property_tree::ptree tree;
nano::jsonconfig tree;
tree.put ("peering_port", std::to_string (0));
tree.put ("packet_delay_microseconds", std::to_string (0));
tree.put ("bootstrap_fraction_numerator", std::to_string (0));
tree.put ("creation_rebroadcast", std::to_string (0));
tree.put ("rebroadcast_delay", std::to_string (0));
tree.put ("receive_minimum", nano::amount (0).to_string_dec ());
boost::property_tree::ptree logging_l;
nano::jsonconfig logging_l;
logging1.serialize_json (logging_l);
tree.add_child ("logging", logging_l);
boost::property_tree::ptree preconfigured_peers_l;
tree.add_child ("preconfigured_peers", preconfigured_peers_l);
boost::property_tree::ptree preconfigured_representatives_l;
tree.add_child ("preconfigured_representatives", preconfigured_representatives_l);
tree.put_child ("logging", logging_l);
nano::jsonconfig preconfigured_peers_l;
tree.put_child ("preconfigured_peers", preconfigured_peers_l);
nano::jsonconfig preconfigured_representatives_l;
tree.put_child ("preconfigured_representatives", preconfigured_representatives_l);
bool upgraded (false);
nano::node_config config1;
config1.logging.init (path);
ASSERT_FALSE (tree.get_child_optional ("work_peers"));
ASSERT_FALSE (tree.get_optional_child ("work_peers"));
config1.deserialize_json (upgraded, tree);
ASSERT_TRUE (upgraded);
ASSERT_TRUE (!!tree.get_child_optional ("work_peers"));
ASSERT_TRUE (!!tree.get_optional_child ("work_peers"));
}
TEST (node_config, v2_v3_upgrade)
@ -569,7 +570,7 @@ TEST (node_config, v2_v3_upgrade)
auto path (nano::unique_path ());
nano::logging logging1;
logging1.init (path);
boost::property_tree::ptree tree;
nano::jsonconfig tree;
tree.put ("peering_port", std::to_string (0));
tree.put ("packet_delay_microseconds", std::to_string (0));
tree.put ("bootstrap_fraction_numerator", std::to_string (0));
@ -577,18 +578,16 @@ TEST (node_config, v2_v3_upgrade)
tree.put ("rebroadcast_delay", std::to_string (0));
tree.put ("receive_minimum", nano::amount (0).to_string_dec ());
tree.put ("version", "2");
boost::property_tree::ptree logging_l;
nano::jsonconfig logging_l;
logging1.serialize_json (logging_l);
tree.add_child ("logging", logging_l);
boost::property_tree::ptree preconfigured_peers_l;
tree.add_child ("preconfigured_peers", preconfigured_peers_l);
boost::property_tree::ptree preconfigured_representatives_l;
boost::property_tree::ptree entry;
entry.put ("", "TR6ZJ4pdp6HC76xMRpVDny5x2s8AEbrhFue3NKVxYYdmKuTEib");
preconfigured_representatives_l.push_back (std::make_pair ("", entry));
tree.add_child ("preconfigured_representatives", preconfigured_representatives_l);
boost::property_tree::ptree work_peers_l;
tree.add_child ("work_peers", work_peers_l);
tree.put_child ("logging", logging_l);
nano::jsonconfig preconfigured_peers_l;
tree.put_child ("preconfigured_peers", preconfigured_peers_l);
nano::jsonconfig preconfigured_representatives_l;
preconfigured_representatives_l.push ("TR6ZJ4pdp6HC76xMRpVDny5x2s8AEbrhFue3NKVxYYdmKuTEib");
tree.put_child ("preconfigured_representatives", preconfigured_representatives_l);
nano::jsonconfig work_peers_l;
tree.put_child ("work_peers", work_peers_l);
bool upgraded (false);
nano::node_config config1;
config1.logging.init (path);

View file

@ -6,6 +6,7 @@
#include <boost/property_tree/ptree.hpp>
#include <boost/thread.hpp>
#include <nano/core_test/testutil.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/node/common.hpp>
#include <nano/node/rpc.hpp>
#include <nano/node/testing.hpp>
@ -1578,7 +1579,7 @@ TEST (rpc_config, serialization)
config1.enable_control = true;
config1.frontier_request_limit = 8192;
config1.chain_request_limit = 4096;
boost::property_tree::ptree tree;
nano::jsonconfig tree;
config1.serialize_json (tree);
nano::rpc_config config2;
ASSERT_NE (config2.address, config1.address);

View file

@ -1,6 +1,7 @@
#include <gtest/gtest.h>
#include <nano/lib/interface.h>
#include <nano/lib/jsonconfig.hpp>
#include <nano/secure/common.hpp>
namespace
@ -427,24 +428,38 @@ TEST (uint256_union, operator_less_than)
test_union_operator_less_than<nano::uint256_union, nano::uint256_t> ();
}
class json_initial_value_test
{
public:
json_initial_value_test (std::string text_a) :
text (std::move (text_a))
{
}
nano::error serialize_json (nano::jsonconfig & json)
{
json.put ("thing", text);
return json.get_error ();
}
std::string text;
};
class json_upgrade_test
{
public:
bool deserialize_json (bool & upgraded, boost::property_tree::ptree & tree_a)
nano::error deserialize_json (bool & upgraded, nano::jsonconfig & json)
{
auto error (false);
if (!tree_a.empty ())
if (!json.empty ())
{
auto text_l (tree_a.get<std::string> ("thing"));
if (text_l == "junktest")
auto text_l (json.get<std::string> ("thing"));
if (text_l == "junktest" || text_l == "created")
{
upgraded = true;
text_l = "changed";
tree_a.put ("thing", text_l);
json.put ("thing", text_l);
}
if (text_l == "error")
{
error = true;
json.get_error () = nano::error_common::generic;
}
text = text_l;
}
@ -452,63 +467,41 @@ public:
{
upgraded = true;
text = "created";
tree_a.put ("thing", text);
json.put ("thing", text);
}
return error;
return json.get_error ();
}
std::string text;
};
TEST (json, fetch_object)
/** Both create and upgrade via read_and_update() */
TEST (json, create_and_upgrade)
{
auto path1 (nano::unique_path ());
std::fstream stream1;
nano::open_or_create (stream1, path1.string ());
stream1 << "{ \"thing\": \"junktest\" }";
stream1.close ();
nano::open_or_create (stream1, path1.string ());
auto path (nano::unique_path ());
nano::jsonconfig json;
json_upgrade_test object1;
auto error1 (nano::fetch_object (object1, path1));
ASSERT_FALSE (error1);
ASSERT_EQ ("changed", object1.text);
boost::property_tree::ptree tree1;
stream1.close ();
nano::open_or_create (stream1, path1.string ());
boost::property_tree::read_json (stream1, tree1);
ASSERT_EQ ("changed", tree1.get<std::string> ("thing"));
std::string string2 ("{ \"thing\": \"junktest2\" }");
std::stringstream stream2 (string2);
ASSERT_FALSE (json.read_and_update (object1, path));
ASSERT_EQ ("created", object1.text);
nano::jsonconfig json2;
json_upgrade_test object2;
auto error2 (nano::fetch_object (object2, stream2));
ASSERT_FALSE (error2);
ASSERT_EQ ("junktest2", object2.text);
ASSERT_EQ ("{ \"thing\": \"junktest2\" }", string2);
std::string string3 ("{ \"thing\": \"error\" }");
std::stringstream stream3 (string3);
json_upgrade_test object3;
auto error3 (nano::fetch_object (object3, stream3));
ASSERT_TRUE (error3);
auto path2 (nano::unique_path ());
std::fstream stream4;
nano::open_or_create (stream4, path2.string ());
json_upgrade_test object4;
auto error4 (nano::fetch_object (object4, path2));
ASSERT_FALSE (error4);
ASSERT_EQ ("created", object4.text);
boost::property_tree::ptree tree2;
stream4.close ();
nano::open_or_create (stream4, path2.string ());
boost::property_tree::read_json (stream4, tree2);
ASSERT_EQ ("created", tree2.get<std::string> ("thing"));
ASSERT_FALSE (json2.read_and_update (object2, path));
ASSERT_EQ ("changed", object2.text);
}
TEST (json, DISABLED_fetch_write_fail)
/** Create config manually, then upgrade via read_and_update() with multiple calls to test idempotence */
TEST (json, upgrade_from_existing)
{
std::string string4 ("");
std::stringstream stream4 (string4, std::ios_base::in);
json_upgrade_test object4;
auto error4 (nano::fetch_object (object4, stream4));
ASSERT_TRUE (error4);
auto path (nano::unique_path ());
nano::jsonconfig json;
json_initial_value_test junktest ("junktest");
junktest.serialize_json (json);
json.write (path);
json_upgrade_test object1;
ASSERT_FALSE (json.read_and_update (object1, path));
ASSERT_EQ ("changed", object1.text);
ASSERT_FALSE (json.read_and_update (object1, path));
ASSERT_EQ ("changed", object1.text);
}
TEST (uint64_t, parse)

View file

@ -1,5 +1,6 @@
#include <gtest/gtest.h>
#include <nano/lib/jsonconfig.hpp>
#include <nano/node/node.hpp>
#include <nano/node/wallet.hpp>
@ -88,7 +89,7 @@ TEST (work, opencl_config)
config1.platform = 1;
config1.device = 2;
config1.threads = 3;
boost::property_tree::ptree tree;
nano::jsonconfig tree;
config1.serialize_json (tree);
nano::opencl_config config2;
ASSERT_FALSE (config2.deserialize_json (tree));

View file

@ -22,6 +22,7 @@ add_library (nano_lib
config.hpp
interface.cpp
interface.h
jsonconfig.hpp
numbers.cpp
numbers.hpp
timer.hpp

View file

@ -20,6 +20,8 @@ std::string nano::error_common_messages::message (int ev) const
return "Missing signature";
case nano::error_common::missing_work:
return "Missing work";
case nano::error_common::exception:
return "Exception thrown";
case nano::error_common::account_exists:
return "Account already exists";
case nano::error_common::account_not_found:
@ -66,6 +68,8 @@ std::string nano::error_common_messages::message (int ev) const
return "Invalid port";
case nano::error_common::invalid_index:
return "Invalid index";
case nano::error_common::invalid_type_conversion:
return "Invalid type conversion";
case nano::error_common::invalid_work:
return "Invalid work";
case nano::error_common::numeric_conversion:
@ -199,3 +203,18 @@ std::string nano::error_process_messages::message (int ev) const
return "Invalid error code";
}
std::string nano::error_config_messages::message (int ev) const
{
switch (static_cast<nano::error_config> (ev))
{
case nano::error_config::generic:
return "Unknown error";
case nano::error_config::invalid_value:
return "Invalid configuration value";
case nano::error_config::missing_value:
return "Missing value in configuration";
}
return "Invalid error code";
}

View file

@ -1,5 +1,8 @@
#pragma once
#include <algorithm>
#include <boost/system/error_code.hpp>
#include <memory>
#include <nano/lib/expected.hpp>
#include <string>
#include <system_error>
@ -10,24 +13,11 @@ using tl::make_unexpected;
namespace nano
{
/** Returns the error code if non-zero, otherwise the value */
template <class T>
auto either (T && value, std::error_code ec) -> expected<typename std::remove_reference<T>::type, std::error_code>
{
if (ec)
{
return make_unexpected (ec);
}
else
{
return std::move (value);
}
}
/** Common error codes */
enum class error_common
{
generic = 1,
exception,
account_not_found,
account_not_found_wallet,
account_exists,
@ -57,6 +47,7 @@ enum class error_common
invalid_index,
invalid_ip_address,
invalid_port,
invalid_type_conversion,
invalid_work,
insufficient_balance,
numeric_conversion,
@ -125,6 +116,29 @@ enum class error_process
block_position, // This block cannot follow the previous block
other
};
/** config.json deserialization related errors */
enum class error_config
{
generic = 1,
invalid_value,
missing_value,
};
} // nano namespace
/** Returns the error code if non-zero, otherwise the value */
template <class T>
auto either (T && value, std::error_code ec) -> expected<typename std::remove_reference_t<T>, std::error_code>
{
if (ec)
{
return make_unexpected (ec);
}
else
{
return std::move (value);
}
}
// Convenience macro to implement the standard boilerplate for using std::error_code with enums
@ -172,3 +186,290 @@ REGISTER_ERROR_CODES (nano, error_common);
REGISTER_ERROR_CODES (nano, error_blocks);
REGISTER_ERROR_CODES (nano, error_rpc);
REGISTER_ERROR_CODES (nano, error_process);
REGISTER_ERROR_CODES (nano, error_config);
/* boost->std error_code bridge */
namespace nano
{
namespace error_conversion
{
const std::error_category & generic_category ();
}
}
namespace std
{
template <>
struct is_error_code_enum<boost::system::errc::errc_t>
: public std::true_type
{
};
inline std::error_code make_error_code (boost::system::errc::errc_t e)
{
return std::error_code (static_cast<int> (e),
::nano::error_conversion::generic_category ());
}
}
namespace nano
{
namespace error_conversion
{
namespace detail
{
class generic_category : public std::error_category
{
public:
virtual const char * name () const noexcept override
{
return boost::system::generic_category ().name ();
}
virtual std::string message (int value) const override
{
return boost::system::generic_category ().message (value);
}
};
}
inline const std::error_category & generic_category ()
{
static detail::generic_category instance;
return instance;
}
inline std::error_code convert (const boost::system::error_code & error)
{
if (error.category () == boost::system::generic_category ())
{
return std::error_code (error.value (),
nano::error_conversion::generic_category ());
}
assert (false);
return nano::error_common::invalid_type_conversion;
}
}
}
namespace nano
{
/** Adapter for std/boost::error_code, std::exception and bool flags to facilitate unified error handling */
class error
{
public:
error () = default;
error (nano::error const & error_a) = default;
error (nano::error && error_a) = default;
error (std::error_code code_a)
{
code = code_a;
}
error (std::string message_a)
{
code = nano::error_common::generic;
message = std::move (message_a);
}
error (std::exception const & exception_a)
{
code = nano::error_common::exception;
message = exception_a.what ();
}
error & operator= (nano::error const & err_a)
{
code = err_a.code;
message = err_a.message;
return *this;
}
error & operator= (nano::error && err_a)
{
code = err_a.code;
message = std::move (err_a.message);
return *this;
}
/** Assign error code */
error & operator= (const std::error_code code_a)
{
code = code_a;
message.clear ();
return *this;
}
/** Assign boost error code (as converted to std::error_code) */
error & operator= (const boost::system::error_code & code_a)
{
code = nano::error_conversion::convert (code_a);
message.clear ();
return *this;
}
/** Assign boost error code (as converted to std::error_code) */
error & operator= (const boost::system::errc::errc_t & code_a)
{
code = nano::error_conversion::convert (boost::system::errc::make_error_code (code_a));
message.clear ();
return *this;
}
/** Set the error to nano::error_common::generic and the error message to \p message_a */
error & operator= (const std::string message_a)
{
code = nano::error_common::generic;
message = std::move (message_a);
return *this;
}
/** Set the error to nano::error_common::generic. */
error & operator= (bool is_error)
{
if (is_error)
{
code = nano::error_common::generic;
}
message.clear ();
return *this;
}
/** Sets the error to nano::error_common::exception and adopts the exception error message. */
error & operator= (std::exception const & exception_a)
{
code = nano::error_common::exception;
message = exception_a.what ();
return *this;
}
/** Return true if this#error_code equals the parameter */
bool operator== (const std::error_code code_a)
{
return code == code_a;
}
/** Return true if this#error_code equals the parameter */
bool operator== (const boost::system::error_code code_a)
{
return code.value () == code_a.value ();
}
/** Call the function iff the current error is zero */
error & then (std::function<nano::error &()> next)
{
return code ? *this : next ();
}
/** If the current error is one of the listed codes, reset the error code */
template <typename... ErrorCode>
error & accept (ErrorCode... err)
{
// Convert variadic arguments to std::error_code
auto codes = { std::error_code (err)... };
if (std::any_of (codes.begin (), codes.end (), [this, &codes](auto & code_a) { return code == code_a; }))
{
code.clear ();
}
return *this;
}
/** Implicit error_code conversion */
explicit operator std::error_code () const
{
return code;
}
/** Implicit bool conversion; true if there's an error */
explicit operator bool () const
{
return code.value () != 0;
}
/** Implicit string conversion; returns the error message or an empty string. */
explicit operator std::string () const
{
return get_message ();
}
/**
* Get error message, or an empty string if there's no error. If a custom error message is set,
* that will be returned, otherwise the error_code#message() is returned.
*/
std::string get_message () const
{
std::string res = message;
if (code && res.empty ())
{
res = code.message ();
}
return res;
}
/** Set an error message, but only if the error code is already set */
error & on_error (std::string message_a)
{
if (code)
{
message = std::move (message_a);
}
return *this;
}
/** Set an error message if the current error code matches \p code_a */
error & on_error (std::error_code code_a, std::string message_a)
{
if (code == code_a)
{
message = std::move (message_a);
}
return *this;
}
/** Set an error message and an error code */
error & set (std::string message_a, std::error_code code_a = nano::error_common::generic)
{
message = message_a;
code = code_a;
return *this;
}
/** Set a custom error message. If the error code is not set, it will be set to nano::error_common::generic. */
error & set_message (std::string message_a)
{
if (!code)
{
code = nano::error_common::generic;
}
message = std::move (message_a);
return *this;
}
/** Clear an errors */
error & clear ()
{
code.clear ();
message.clear ();
return *this;
}
private:
std::error_code code;
std::string message;
};
/**
* A type that manages a nano::error.
* The default return type is nano::error&, though shared_ptr<nano::error> is a good option in cases
* where shared error state is desirable.
*/
template <typename RET_TYPE = nano::error &>
class error_aware
{
static_assert (std::is_same<RET_TYPE, nano::error &>::value || std::is_same<RET_TYPE, std::shared_ptr<nano::error>>::value, "Must be nano::error& or shared_ptr<nano::error>");
public:
/** Returns the error object managed by this object */
virtual RET_TYPE get_error () = 0;
};
}

487
nano/lib/jsonconfig.hpp Normal file
View file

@ -0,0 +1,487 @@
#pragma once
#include <boost/asio.hpp>
#include <boost/filesystem.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/optional.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <fstream>
#include <nano/lib/errors.hpp>
namespace nano
{
/** Type trait to determine if T is compatible with boost's lexical_cast */
template <class T>
struct is_lexical_castable : std::integral_constant<bool,
(std::is_default_constructible<T>::value && (boost::has_right_shift<std::basic_istream<wchar_t>, T>::value || boost::has_right_shift<std::basic_istream<char>, T>::value))>
{
};
/* Type descriptions are used to automatically construct configuration error messages */
// clang-format off
template <typename T> inline std::string type_desc (void) { return "an unknown type"; }
template <> inline std::string type_desc<int8_t> (void) { return "an integer between -128 and 127"; }
template <> inline std::string type_desc<uint8_t> (void) { return "an integer between 0 and 255"; }
template <> inline std::string type_desc<int16_t> (void) { return "an integer between -32768 and 32767"; }
template <> inline std::string type_desc<uint16_t> (void) { return "an integer between 0 and 65535"; }
template <> inline std::string type_desc<int32_t> (void) { return "a 32-bit signed integer"; }
template <> inline std::string type_desc<uint32_t> (void) { return "a 32-bit unsigned integer"; }
template <> inline std::string type_desc<int64_t> (void) { return "a 64-bit signed integer"; }
template <> inline std::string type_desc<uint64_t> (void) { return "a 64-bit unsigned integer"; }
template <> inline std::string type_desc<float> (void) { return "a single precision floating point number"; }
template <> inline std::string type_desc<double> (void) { return "a double precison floating point number"; }
template <> inline std::string type_desc<char> (void) { return "a character"; }
template <> inline std::string type_desc<std::string> (void) { return "a string"; }
template <> inline std::string type_desc<bool> (void) { return "a boolean"; }
template <> inline std::string type_desc<boost::asio::ip::address_v6> (void) { return "an IP address"; }
// clang-format on
/** Manages a node in a boost configuration tree. */
class jsonconfig : public nano::error_aware<>
{
public:
jsonconfig ()
{
error = std::make_shared<nano::error> ();
}
jsonconfig (boost::property_tree::ptree const & tree_a, std::shared_ptr<nano::error> error_a = nullptr) :
tree (tree_a), error (error_a)
{
if (!error)
{
error = std::make_shared<nano::error> ();
}
}
/**
* Reads a json object from the stream and if it was changed, write the object back to the stream.
* @return nano::error&, including a descriptive error message if the config file is malformed.
*/
template <typename T>
nano::error & read_and_update (T & object, boost::filesystem::path const & path_a)
{
std::fstream stream;
open_or_create (stream, path_a.string ());
if (!stream.fail ())
{
try
{
boost::property_tree::read_json (stream, tree);
}
catch (std::runtime_error const & ex)
{
auto pos (stream.tellg ());
if (pos != std::streampos (0))
{
*error = ex;
}
}
stream.close ();
if (!*error)
{
auto updated (false);
*error = object.deserialize_json (updated, *this);
if (!*error && updated)
{
stream.open (path_a.string (), std::ios_base::out | std::ios_base::trunc);
try
{
boost::property_tree::write_json (stream, tree);
}
catch (std::runtime_error const & ex)
{
*error = ex;
}
stream.close ();
}
}
}
return *error;
}
void write (boost::filesystem::path const & path_a)
{
std::fstream stream;
open_or_create (stream, path_a.string ());
write (stream);
}
void write (std::ostream & stream_a) const
{
boost::property_tree::write_json (stream_a, tree);
}
void read (std::istream & stream_a)
{
boost::property_tree::read_json (stream_a, tree);
}
/** Open confiruation file, create if necessary */
void open_or_create (std::fstream & stream_a, std::string const & path_a)
{
stream_a.open (path_a, std::ios_base::in);
if (stream_a.fail ())
{
stream_a.open (path_a, std::ios_base::out);
}
stream_a.close ();
stream_a.open (path_a, std::ios_base::in | std::ios_base::out);
}
/** Returns the boost property node managed by this instance */
boost::property_tree::ptree const & get_tree ()
{
return tree;
}
/** Returns true if the property tree node is empty */
bool empty () const
{
return tree.empty ();
}
/** Optionally returns the given child node */
jsonconfig & get_optional_child (std::string const & key_a, boost::optional<jsonconfig> & child_config)
{
auto child = tree.get_child_optional (key_a);
if (child)
{
child_config = jsonconfig (child.get (), error);
}
return *this;
}
boost::optional<jsonconfig> get_optional_child (std::string const & key_a)
{
boost::optional<jsonconfig> child_config;
auto child = tree.get_child_optional (key_a);
if (child)
{
child_config = jsonconfig (child.get (), error);
}
return child_config;
}
jsonconfig & get_required_child (std::string const & key_a, jsonconfig & child_config /*out*/)
{
auto child = tree.get_child_optional (key_a);
if (child)
{
child_config = jsonconfig (child.get (), error);
}
else if (!*error)
{
*error = nano::error_config::missing_value;
error->set_message ("Missing configuration node: " + key_a);
}
return *this;
}
jsonconfig get_required_child (std::string const & key_a)
{
jsonconfig child_config;
get_required_child (key_a, child_config);
return child_config;
}
jsonconfig & put_child (std::string const & key_a, nano::jsonconfig & conf_a)
{
tree.add_child (key_a, conf_a.get_tree ());
return *this;
}
jsonconfig & replace_child (std::string const & key_a, nano::jsonconfig & conf_a)
{
conf_a.erase (key_a);
conf_a.put_child (key_a, conf_a);
return *this;
}
/** Set value for the given key. Any existing value will be overwritten. */
template <typename T>
jsonconfig & put (std::string const & key, T const & value)
{
tree.put (key, value);
return *this;
}
/** Push array element */
template <typename T>
jsonconfig & push (T const & value)
{
boost::property_tree::ptree entry;
entry.put ("", value);
tree.push_back (std::make_pair ("", entry));
return *this;
}
/** Iterate array entries */
template <typename T>
jsonconfig & array_entries (std::function<void(T)> callback)
{
for (auto & entry : tree)
{
callback (entry.second.get<T> (""));
}
return *this;
}
/** Returns true if \p key_a is present */
bool has_key (std::string const & key_a)
{
return tree.find (key_a) != tree.not_found ();
}
/** Erase the property of given key */
jsonconfig & erase (std::string const & key_a)
{
tree.erase (key_a);
return *this;
}
/** Get optional, using \p default_value if \p key is missing. */
template <typename T>
jsonconfig & get_optional (std::string const & key, T & target, T default_value)
{
get_config<T> (true, key, target, default_value);
return *this;
}
/**
* Get optional value, using the current value of \p target as the default if \p key is missing.
* @return May return nano::error_config::invalid_value
*/
template <typename T>
jsonconfig & get_optional (std::string const & key, T & target)
{
get_config<T> (true, key, target, target);
return *this;
}
/** Return a boost::optional<T> for the given key */
template <typename T>
boost::optional<T> get_optional (std::string const & key)
{
boost::optional<T> res;
if (has_key (key))
{
T target{};
get_config<T> (true, key, target, target);
res = target;
}
return res;
}
/** Get value, using the current value of \p target as the default if \p key is missing. */
template <typename T>
jsonconfig & get (std::string const & key, T & target)
{
get_config<T> (true, key, target, target);
return *this;
}
/**
* Get value of required key
* @note May set nano::error_config::missing_value if \p key is missing, nano::error_config::invalid_value if value is invalid.
*/
template <typename T>
T get (std::string const & key)
{
T target{};
get_config<T> (true, key, target, target);
return target;
}
/**
* Get required value.
* @note May set nano::error_config::missing_value if \p key is missing, nano::error_config::invalid_value if value is invalid.
*/
template <typename T>
jsonconfig & get_required (std::string const & key, T & target)
{
get_config<T> (false, key, target);
return *this;
}
/** Turn on or off automatic error message generation */
void set_auto_error_message (bool auto_a)
{
auto_error_message = auto_a;
}
nano::error & get_error () override
{
return *error;
}
protected:
template <typename T>
void construct_error_message (bool optional, std::string const & key)
{
if (auto_error_message && *error)
{
if (optional)
{
error->set_message (key + " is not " + type_desc<T> ());
}
else
{
error->set_message (key + " is required and must be " + type_desc<T> ());
}
}
}
/** Set error if not already set. That is, first error remains until get_error().clear() is called. */
template <typename T, typename V>
void conditionally_set_error (V error_a, bool optional, std::string const & key)
{
if (!*error)
{
*error = error_a;
construct_error_message<T> (optional, key);
}
}
template <typename T, typename = std::enable_if_t<nano::is_lexical_castable<T>::value>>
jsonconfig & get_config (bool optional, std::string key, T & target, T default_value = T ())
{
try
{
auto val (tree.get<std::string> (key));
if (!boost::conversion::try_lexical_convert<T> (val, target))
{
conditionally_set_error<T> (nano::error_config::invalid_value, optional, key);
}
}
catch (boost::property_tree::ptree_bad_path const & ex)
{
if (!optional)
{
conditionally_set_error<T> (nano::error_config::missing_value, optional, key);
}
else
{
target = default_value;
}
}
catch (std::runtime_error & ex)
{
conditionally_set_error<T> (ex, optional, key);
}
return *this;
}
// boost's lexical cast doesn't handle (u)int8_t
template <typename T, typename = std::enable_if_t<std::is_same<T, uint8_t>::value>>
jsonconfig & get_config (bool optional, std::string key, uint8_t & target, uint8_t default_value = T ())
{
int64_t tmp;
try
{
auto val (tree.get<std::string> (key));
if (!boost::conversion::try_lexical_convert<int64_t> (val, tmp) || tmp < 0 || tmp > 255)
{
conditionally_set_error<T> (nano::error_config::invalid_value, optional, key);
}
else
{
target = static_cast<uint8_t> (tmp);
}
}
catch (boost::property_tree::ptree_bad_path const & ex)
{
if (!optional)
{
conditionally_set_error<T> (nano::error_config::missing_value, optional, key);
}
else
{
target = default_value;
}
}
catch (std::runtime_error & ex)
{
conditionally_set_error<T> (ex, optional, key);
}
return *this;
}
template <typename T, typename = std::enable_if_t<std::is_same<T, bool>::value>>
jsonconfig & get_config (bool optional, std::string key, bool & target, bool default_value = false)
{
auto bool_conv = [this, &target, &key, optional](std::string val) {
if (val == "true")
{
target = true;
}
else if (val == "false")
{
target = false;
}
else if (!*error)
{
conditionally_set_error<T> (nano::error_config::invalid_value, optional, key);
}
};
try
{
auto val (tree.get<std::string> (key));
bool_conv (val);
}
catch (boost::property_tree::ptree_bad_path const & ex)
{
if (!optional)
{
conditionally_set_error<T> (nano::error_config::missing_value, optional, key);
}
else
{
target = default_value;
}
}
catch (std::runtime_error & ex)
{
conditionally_set_error<T> (ex, optional, key);
}
return *this;
}
template <typename T, typename = std::enable_if_t<std::is_same<T, boost::asio::ip::address_v6>::value>>
jsonconfig & get_config (bool optional, std::string key, boost::asio::ip::address_v6 & target, boost::asio::ip::address_v6 default_value = T ())
{
try
{
auto address_l (tree.get<std::string> (key));
boost::system::error_code bec;
target = boost::asio::ip::address_v6::from_string (address_l, bec);
if (bec)
{
conditionally_set_error<T> (nano::error_config::invalid_value, optional, key);
}
}
catch (boost::property_tree::ptree_bad_path const & ex)
{
if (!optional)
{
conditionally_set_error<T> (nano::error_config::missing_value, optional, key);
}
else
{
target = default_value;
}
}
return *this;
}
private:
/** The property node being managed */
boost::property_tree::ptree tree;
/** If set, automatically construct error messages based on parameters and type information. */
bool auto_error_message{ true };
/** We're a nano::error_aware type. Child nodes share the error state. */
std::shared_ptr<nano::error> error;
};
}

View file

@ -1,3 +1,4 @@
#include <nano/lib/jsonconfig.hpp>
#include <nano/lib/utility.hpp>
#include <nano/nano_node/daemon.hpp>
@ -12,86 +13,104 @@ opencl_enable (false)
{
}
void nano_daemon::daemon_config::serialize_json (boost::property_tree::ptree & tree_a)
nano::error nano_daemon::daemon_config::serialize_json (nano::jsonconfig & json)
{
tree_a.put ("version", std::to_string (json_version));
tree_a.put ("rpc_enable", rpc_enable);
boost::property_tree::ptree rpc_l;
json.put ("version", json_version ());
json.put ("rpc_enable", rpc_enable);
nano::jsonconfig rpc_l;
rpc.serialize_json (rpc_l);
tree_a.add_child ("rpc", rpc_l);
boost::property_tree::ptree node_l;
json.put_child ("rpc", rpc_l);
nano::jsonconfig node_l;
node.serialize_json (node_l);
tree_a.add_child ("node", node_l);
tree_a.put ("opencl_enable", opencl_enable);
boost::property_tree::ptree opencl_l;
nano::jsonconfig node (node_l);
json.put_child ("node", node);
json.put ("opencl_enable", opencl_enable);
nano::jsonconfig opencl_l;
opencl.serialize_json (opencl_l);
tree_a.add_child ("opencl", opencl_l);
json.put_child ("opencl", opencl_l);
return json.get_error ();
}
bool nano_daemon::daemon_config::deserialize_json (bool & upgraded_a, boost::property_tree::ptree & tree_a)
nano::error nano_daemon::daemon_config::deserialize_json (bool & upgraded_a, nano::jsonconfig & json)
{
auto error (false);
try
{
if (!tree_a.empty ())
if (!json.empty ())
{
auto version_l (tree_a.get_optional<std::string> ("version"));
if (!version_l)
int version_l;
json.get_optional<int> ("version", version_l);
upgraded_a |= upgrade_json (version_l, json);
json.get_optional<bool> ("rpc_enable", rpc_enable);
nano::jsonconfig rpc_l;
json.get_required_child ("rpc", rpc_l);
if (!rpc.deserialize_json (rpc_l))
{
tree_a.put ("version", "1");
version_l = "1";
nano::jsonconfig node_l;
json.get_required_child ("node", node_l);
if (!json.get_error ())
{
node.deserialize_json (upgraded_a, node_l);
}
}
if (!json.get_error ())
{
json.get_required<bool> ("opencl_enable", opencl_enable);
nano::jsonconfig opencl_l;
json.get_required_child ("opencl", opencl_l);
if (!json.get_error ())
{
opencl.deserialize_json (opencl_l);
}
}
upgraded_a |= upgrade_json (std::stoull (version_l.get ()), tree_a);
rpc_enable = tree_a.get<bool> ("rpc_enable");
auto rpc_l (tree_a.get_child ("rpc"));
error |= rpc.deserialize_json (rpc_l);
auto & node_l (tree_a.get_child ("node"));
error |= node.deserialize_json (upgraded_a, node_l);
opencl_enable = tree_a.get<bool> ("opencl_enable");
auto & opencl_l (tree_a.get_child ("opencl"));
error |= opencl.deserialize_json (opencl_l);
}
else
{
upgraded_a = true;
serialize_json (tree_a);
serialize_json (json);
}
}
catch (std::runtime_error const &)
catch (std::runtime_error const & ex)
{
error = true;
json.get_error () = ex;
}
return error;
return json.get_error ();
}
bool nano_daemon::daemon_config::upgrade_json (unsigned version_a, boost::property_tree::ptree & tree_a)
bool nano_daemon::daemon_config::upgrade_json (unsigned version_a, nano::jsonconfig & json)
{
tree_a.put ("version", std::to_string (json_version));
auto result (false);
json.put ("version", json_version ());
auto upgraded_l (false);
switch (version_a)
{
case 1:
{
auto opencl_enable_l (tree_a.get_optional<bool> ("opencl_enable"));
bool opencl_enable_l;
json.get_optional<bool> ("opencl_enable", opencl_enable_l);
if (!opencl_enable_l)
{
tree_a.put ("opencl_enable", "false");
json.put ("opencl_enable", false);
}
auto opencl_l (tree_a.get_child_optional ("opencl"));
boost::optional<nano::jsonconfig> opencl_l;
json.get_optional_child ("opencl", opencl_l);
if (!opencl_l)
{
boost::property_tree::ptree opencl_l;
nano::jsonconfig opencl_l;
opencl.serialize_json (opencl_l);
tree_a.put_child ("opencl", opencl_l);
json.put_child ("opencl", opencl_l);
}
result = true;
upgraded_l = true;
}
case 2:
break;
default:
throw std::runtime_error ("Unknown daemon_config version");
}
return result;
return upgraded_l;
}
void nano_daemon::daemon::run (boost::filesystem::path const & data_path, nano::node_flags const & flags)
@ -102,7 +121,8 @@ void nano_daemon::daemon::run (boost::filesystem::path const & data_path, nano::
nano::set_secure_perm_directory (data_path, error_chmod);
auto config_path ((data_path / "config.json"));
std::unique_ptr<nano::thread_runner> runner;
auto error (nano::fetch_object (config, config_path));
nano::jsonconfig json;
auto error (json.read_and_update (config, config_path));
nano::set_secure_perm_file (config_path, error_chmod);
if (!error)
{
@ -142,6 +162,6 @@ void nano_daemon::daemon::run (boost::filesystem::path const & data_path, nano::
}
else
{
std::cerr << "Error deserializing config\n";
std::cerr << "Error deserializing config: " << error.get_message () << std::endl;
}
}

View file

@ -1,3 +1,4 @@
#include <nano/lib/errors.hpp>
#include <nano/node/node.hpp>
#include <nano/node/rpc.hpp>
@ -12,14 +13,22 @@ class daemon_config
{
public:
daemon_config ();
bool deserialize_json (bool &, boost::property_tree::ptree &);
void serialize_json (boost::property_tree::ptree &);
bool upgrade_json (unsigned, boost::property_tree::ptree &);
nano::error deserialize_json (bool &, nano::jsonconfig &);
nano::error serialize_json (nano::jsonconfig &);
/**
* Returns true if an upgrade occurred
* @param version The version to upgrade to.
* @param config Configuration to upgrade.
*/
bool upgrade_json (unsigned version, nano::jsonconfig & config);
bool rpc_enable;
nano::rpc_config rpc;
nano::node_config node;
bool opencl_enable;
nano::opencl_config opencl;
static constexpr int json_version = 2;
int json_version () const
{
return 2;
}
};
}

View file

@ -1,3 +1,5 @@
#include <nano/lib/errors.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/lib/utility.hpp>
#include <nano/nano_wallet/icon.hpp>
#include <nano/node/cli.hpp>
@ -21,135 +23,139 @@ public:
nano::random_pool.GenerateBlock (wallet.bytes.data (), wallet.bytes.size ());
assert (!wallet.is_zero ());
}
bool upgrade_json (unsigned version_a, boost::property_tree::ptree & tree_a)
bool upgrade_json (unsigned version_a, nano::jsonconfig & json)
{
tree_a.put ("version", std::to_string (json_version));
auto result (false);
json.put ("version", json_version ());
auto upgraded (false);
switch (version_a)
{
case 1:
{
nano::account account;
account.decode_account (tree_a.get<std::string> ("account"));
tree_a.erase ("account");
tree_a.put ("account", account.to_account ());
tree_a.erase ("version");
result = true;
account.decode_account (json.get<std::string> ("account"));
json.erase ("account");
json.put ("account", account.to_account ());
json.erase ("version");
upgraded = true;
}
case 2:
{
boost::property_tree::ptree rpc_l;
nano::jsonconfig rpc_l;
rpc.serialize_json (rpc_l);
tree_a.put ("rpc_enable", "false");
tree_a.put_child ("rpc", rpc_l);
tree_a.erase ("version");
result = true;
json.put ("rpc_enable", "false");
json.put_child ("rpc", rpc_l);
json.erase ("version");
upgraded = true;
}
case 3:
{
auto opencl_enable_l (tree_a.get_optional<bool> ("opencl_enable"));
auto opencl_enable_l (json.get_optional<bool> ("opencl_enable"));
if (!opencl_enable_l)
{
tree_a.put ("opencl_enable", "false");
json.put ("opencl_enable", "false");
}
auto opencl_l (tree_a.get_child_optional ("opencl"));
auto opencl_l (json.get_optional_child ("opencl"));
if (!opencl_l)
{
boost::property_tree::ptree opencl_l;
nano::jsonconfig opencl_l;
opencl.serialize_json (opencl_l);
tree_a.put_child ("opencl", opencl_l);
json.put_child ("opencl", opencl_l);
}
result = true;
upgraded = true;
}
case 4:
break;
default:
throw std::runtime_error ("Unknown qt_wallet_config version");
}
return result;
return upgraded;
}
bool deserialize_json (bool & upgraded_a, boost::property_tree::ptree & tree_a)
nano::error deserialize_json (bool & upgraded_a, nano::jsonconfig & json)
{
auto error (false);
if (!tree_a.empty ())
if (!json.empty ())
{
auto version_l (tree_a.get_optional<std::string> ("version"));
auto version_l (json.get_optional<unsigned> ("version"));
if (!version_l)
{
tree_a.put ("version", "1");
version_l = "1";
version_l = 1;
json.put ("version", version_l.get ());
upgraded_a = true;
}
upgraded_a |= upgrade_json (std::stoull (version_l.get ()), tree_a);
auto wallet_l (tree_a.get<std::string> ("wallet"));
auto account_l (tree_a.get<std::string> ("account"));
auto & node_l (tree_a.get_child ("node"));
rpc_enable = tree_a.get<bool> ("rpc_enable");
auto & rpc_l (tree_a.get_child ("rpc"));
opencl_enable = tree_a.get<bool> ("opencl_enable");
auto & opencl_l (tree_a.get_child ("opencl"));
try
upgraded_a |= upgrade_json (version_l.get (), json);
auto wallet_l (json.get<std::string> ("wallet"));
auto account_l (json.get<std::string> ("account"));
auto node_l (json.get_required_child ("node"));
rpc_enable = json.get<bool> ("rpc_enable");
auto rpc_l (json.get_required_child ("rpc"));
opencl_enable = json.get<bool> ("opencl_enable");
auto opencl_l (json.get_required_child ("opencl"));
if (wallet.decode_hex (wallet_l))
{
error |= wallet.decode_hex (wallet_l);
error |= account.decode_account (account_l);
error |= node.deserialize_json (upgraded_a, node_l);
error |= rpc.deserialize_json (rpc_l);
error |= opencl.deserialize_json (opencl_l);
if (wallet.is_zero ())
{
nano::random_pool.GenerateBlock (wallet.bytes.data (), wallet.bytes.size ());
upgraded_a = true;
}
json.get_error ().set ("Invalid wallet id. Did you open a node daemon config?");
}
catch (std::logic_error const &)
else if (account.decode_account (account_l))
{
error = true;
json.get_error ().set ("Invalid account");
}
node.deserialize_json (upgraded_a, node_l);
rpc.deserialize_json (rpc_l);
opencl.deserialize_json (opencl_l);
if (wallet.is_zero ())
{
nano::random_pool.GenerateBlock (wallet.bytes.data (), wallet.bytes.size ());
upgraded_a = true;
}
}
else
{
serialize_json (tree_a);
serialize_json (json);
upgraded_a = true;
}
return error;
return json.get_error ();
}
void serialize_json (boost::property_tree::ptree & tree_a)
void serialize_json (nano::jsonconfig & json)
{
std::string wallet_string;
wallet.encode_hex (wallet_string);
tree_a.put ("version", std::to_string (json_version));
tree_a.put ("wallet", wallet_string);
tree_a.put ("account", account.to_account ());
boost::property_tree::ptree node_l;
json.put ("version", json_version ());
json.put ("wallet", wallet_string);
json.put ("account", account.to_account ());
nano::jsonconfig node_l;
node.enable_voting = false;
node.bootstrap_connections_max = 4;
node.serialize_json (node_l);
tree_a.add_child ("node", node_l);
boost::property_tree::ptree rpc_l;
json.put_child ("node", node_l);
nano::jsonconfig rpc_l;
rpc.serialize_json (rpc_l);
tree_a.add_child ("rpc", rpc_l);
tree_a.put ("rpc_enable", rpc_enable);
tree_a.put ("opencl_enable", opencl_enable);
boost::property_tree::ptree opencl_l;
json.put_child ("rpc", rpc_l);
json.put ("rpc_enable", rpc_enable);
json.put ("opencl_enable", opencl_enable);
nano::jsonconfig opencl_l;
opencl.serialize_json (opencl_l);
tree_a.add_child ("opencl", opencl_l);
json.put_child ("opencl", opencl_l);
}
bool serialize_json_stream (std::ostream & stream_a)
{
auto result (false);
stream_a.seekp (0);
try
{
boost::property_tree::ptree tree;
serialize_json (tree);
boost::property_tree::write_json (stream_a, tree);
nano::jsonconfig json;
serialize_json (json);
json.write (stream_a);
}
catch (std::runtime_error const &)
catch (std::runtime_error const & ex)
{
std::cerr << ex.what () << std::endl;
result = true;
}
return result;
}
nano::uint256_union wallet;
nano::account account;
nano::node_config node;
@ -157,7 +163,10 @@ public:
nano::rpc_config rpc;
bool opencl_enable;
nano::opencl_config opencl;
static constexpr int json_version = 4;
int json_version () const
{
return 4;
}
};
namespace
@ -169,19 +178,23 @@ void show_error (std::string const & message_a)
message.show ();
message.exec ();
}
bool update_config (qt_wallet_config & config_a, boost::filesystem::path const & config_path_a, std::fstream & config_file_a)
bool update_config (qt_wallet_config & config_a, boost::filesystem::path const & config_path_a)
{
auto account (config_a.account);
auto wallet (config_a.wallet);
auto error (false);
if (!nano::fetch_object (config_a, config_path_a))
nano::jsonconfig config;
if (!config.read_and_update (config_a, config_path_a))
{
if (account != config_a.account || wallet != config_a.wallet)
{
config_a.account = account;
config_a.wallet = wallet;
config_file_a.open (config_path_a.string (), std::ios_base::out | std::ios_base::trunc);
error = config_a.serialize_json_stream (config_file_a);
// Update json file with new account and/or wallet values
std::fstream config_file;
config_file.open (config_path_a.string (), std::ios_base::out | std::ios_base::trunc);
error = config_a.serialize_json_stream (config_file);
}
}
return error;
@ -203,9 +216,8 @@ int run_wallet (QApplication & application, int argc, char * const * argv, boost
qt_wallet_config config (data_path);
auto config_path ((data_path / "config.json"));
int result (0);
std::fstream config_file;
auto error (nano::fetch_object (config, config_path));
config_file.close ();
nano::jsonconfig json;
auto error (json.read_and_update (config, config_path));
nano::set_secure_perm_file (config_path, error_chmod);
if (!error)
{
@ -253,7 +265,7 @@ int run_wallet (QApplication & application, int argc, char * const * argv, boost
}
}
assert (wallet->exists (config.account));
update_config (config, config_path, config_file);
update_config (config, config_path);
node->start ();
std::unique_ptr<nano::rpc> rpc = get_rpc (io_ctx, *node, config.rpc);
if (rpc && config.rpc_enable)
@ -276,13 +288,15 @@ int run_wallet (QApplication & application, int argc, char * const * argv, boost
}
else
{
splash->hide ();
show_error ("Error initializing node");
}
update_config (config, config_path, config_file);
update_config (config, config_path);
}
else
{
show_error ("Error deserializing config");
splash->hide ();
show_error ("Error deserializing config: " + json.get_error ().get_message ());
}
return result;
}

View file

@ -2,6 +2,7 @@
#include <boost/log/utility/setup/common_attributes.hpp>
#include <boost/log/utility/setup/console.hpp>
#include <boost/log/utility/setup/file.hpp>
#include <boost/property_tree/ptree.hpp>
#include <nano/node/logging.hpp>
nano::logging::logging () :
@ -42,104 +43,106 @@ void nano::logging::init (boost::filesystem::path const & application_path_a)
}
}
void nano::logging::serialize_json (boost::property_tree::ptree & tree_a) const
nano::error nano::logging::serialize_json (nano::jsonconfig & json) const
{
tree_a.put ("version", std::to_string (json_version));
tree_a.put ("ledger", ledger_logging_value);
tree_a.put ("ledger_duplicate", ledger_duplicate_logging_value);
tree_a.put ("vote", vote_logging_value);
tree_a.put ("network", network_logging_value);
tree_a.put ("network_message", network_message_logging_value);
tree_a.put ("network_publish", network_publish_logging_value);
tree_a.put ("network_packet", network_packet_logging_value);
tree_a.put ("network_keepalive", network_keepalive_logging_value);
tree_a.put ("network_node_id_handshake", network_node_id_handshake_logging_value);
tree_a.put ("node_lifetime_tracing", node_lifetime_tracing_value);
tree_a.put ("insufficient_work", insufficient_work_logging_value);
tree_a.put ("log_rpc", log_rpc_value);
tree_a.put ("bulk_pull", bulk_pull_logging_value);
tree_a.put ("work_generation_time", work_generation_time_value);
tree_a.put ("upnp_details", upnp_details_logging_value);
tree_a.put ("timing", timing_logging_value);
tree_a.put ("log_to_cerr", log_to_cerr_value);
tree_a.put ("max_size", max_size);
tree_a.put ("rotation_size", rotation_size);
tree_a.put ("flush", flush);
json.put ("version", json_version ());
json.put ("ledger", ledger_logging_value);
json.put ("ledger_duplicate", ledger_duplicate_logging_value);
json.put ("vote", vote_logging_value);
json.put ("network", network_logging_value);
json.put ("network_message", network_message_logging_value);
json.put ("network_publish", network_publish_logging_value);
json.put ("network_packet", network_packet_logging_value);
json.put ("network_keepalive", network_keepalive_logging_value);
json.put ("network_node_id_handshake", network_node_id_handshake_logging_value);
json.put ("node_lifetime_tracing", node_lifetime_tracing_value);
json.put ("insufficient_work", insufficient_work_logging_value);
json.put ("log_rpc", log_rpc_value);
json.put ("bulk_pull", bulk_pull_logging_value);
json.put ("work_generation_time", work_generation_time_value);
json.put ("upnp_details", upnp_details_logging_value);
json.put ("timing", timing_logging_value);
json.put ("log_to_cerr", log_to_cerr_value);
json.put ("max_size", max_size);
json.put ("rotation_size", rotation_size);
json.put ("flush", flush);
return json.get_error ();
}
bool nano::logging::upgrade_json (unsigned version_a, boost::property_tree::ptree & tree_a)
bool nano::logging::upgrade_json (unsigned version_a, nano::jsonconfig & json)
{
tree_a.put ("version", std::to_string (json_version));
auto result (false);
json.put ("version", json_version ());
auto upgraded_l (false);
switch (version_a)
{
case 1:
tree_a.put ("vote", vote_logging_value);
result = true;
json.put ("vote", vote_logging_value);
upgraded_l = true;
case 2:
tree_a.put ("rotation_size", "4194304");
tree_a.put ("flush", "true");
result = true;
json.put ("rotation_size", rotation_size);
json.put ("flush", true);
upgraded_l = true;
case 3:
tree_a.put ("network_node_id_handshake", "false");
result = true;
json.put ("network_node_id_handshake", false);
upgraded_l = true;
case 4:
tree_a.put ("upnp_details", "false");
tree_a.put ("timing", "false");
result = true;
json.put ("upnp_details", "false");
json.put ("timing", "false");
upgraded_l = true;
case 5:
break;
default:
throw std::runtime_error ("Unknown logging_config version");
break;
}
return result;
return upgraded_l;
}
bool nano::logging::deserialize_json (bool & upgraded_a, boost::property_tree::ptree & tree_a)
nano::error nano::logging::deserialize_json (bool & upgraded_a, nano::jsonconfig & json)
{
auto result (false);
try
int version_l;
if (!json.has_key ("version"))
{
auto version_l (tree_a.get_optional<std::string> ("version"));
if (!version_l)
version_l = 1;
json.put ("version", version_l);
boost::optional<nano::jsonconfig> work_peers_l;
json.get_optional_child ("work_peers", work_peers_l);
if (!work_peers_l)
{
tree_a.put ("version", "1");
version_l = "1";
auto work_peers_l (tree_a.get_child_optional ("work_peers"));
if (!work_peers_l)
{
tree_a.add_child ("work_peers", boost::property_tree::ptree ());
}
upgraded_a = true;
nano::jsonconfig peers;
json.put_child ("work_peers", peers);
}
upgraded_a |= upgrade_json (std::stoull (version_l.get ()), tree_a);
ledger_logging_value = tree_a.get<bool> ("ledger");
ledger_duplicate_logging_value = tree_a.get<bool> ("ledger_duplicate");
vote_logging_value = tree_a.get<bool> ("vote");
network_logging_value = tree_a.get<bool> ("network");
network_message_logging_value = tree_a.get<bool> ("network_message");
network_publish_logging_value = tree_a.get<bool> ("network_publish");
network_packet_logging_value = tree_a.get<bool> ("network_packet");
network_keepalive_logging_value = tree_a.get<bool> ("network_keepalive");
network_node_id_handshake_logging_value = tree_a.get<bool> ("network_node_id_handshake");
node_lifetime_tracing_value = tree_a.get<bool> ("node_lifetime_tracing");
insufficient_work_logging_value = tree_a.get<bool> ("insufficient_work");
log_rpc_value = tree_a.get<bool> ("log_rpc");
bulk_pull_logging_value = tree_a.get<bool> ("bulk_pull");
work_generation_time_value = tree_a.get<bool> ("work_generation_time");
upnp_details_logging_value = tree_a.get<bool> ("upnp_details");
timing_logging_value = tree_a.get<bool> ("timing");
log_to_cerr_value = tree_a.get<bool> ("log_to_cerr");
max_size = tree_a.get<uintmax_t> ("max_size");
rotation_size = tree_a.get<uintmax_t> ("rotation_size", 4194304);
flush = tree_a.get<bool> ("flush", true);
upgraded_a = true;
}
catch (std::runtime_error const &)
else
{
result = true;
json.get_required<int> ("version", version_l);
}
return result;
upgraded_a |= upgrade_json (version_l, json);
json.get<bool> ("ledger", ledger_logging_value);
json.get<bool> ("ledger_duplicate", ledger_duplicate_logging_value);
json.get<bool> ("vote", vote_logging_value);
json.get<bool> ("network", network_logging_value);
json.get<bool> ("network_message", network_message_logging_value);
json.get<bool> ("network_publish", network_publish_logging_value);
json.get<bool> ("network_packet", network_packet_logging_value);
json.get<bool> ("network_keepalive", network_keepalive_logging_value);
json.get<bool> ("network_node_id_handshake", network_node_id_handshake_logging_value);
json.get<bool> ("node_lifetime_tracing", node_lifetime_tracing_value);
json.get<bool> ("insufficient_work", insufficient_work_logging_value);
json.get<bool> ("log_rpc", log_rpc_value);
json.get<bool> ("bulk_pull", bulk_pull_logging_value);
json.get<bool> ("work_generation_time", work_generation_time_value);
json.get<bool> ("upnp_details", upnp_details_logging_value);
json.get<bool> ("timing", timing_logging_value);
json.get<bool> ("log_to_cerr", log_to_cerr_value);
json.get<bool> ("flush", flush);
json.get<uintmax_t> ("max_size", max_size);
json.get<uintmax_t> ("rotation_size", rotation_size);
return json.get_error ();
}
bool nano::logging::ledger_logging () const

View file

@ -3,8 +3,9 @@
#include <boost/filesystem.hpp>
#include <boost/log/sources/logger.hpp>
#include <boost/log/trivial.hpp>
#include <boost/property_tree/ptree.hpp>
#include <cstdint>
#include <nano/lib/errors.hpp>
#include <nano/lib/jsonconfig.hpp>
#define FATAL_LOG_PREFIX "FATAL ERROR: "
@ -14,9 +15,9 @@ class logging
{
public:
logging ();
void serialize_json (boost::property_tree::ptree &) const;
bool deserialize_json (bool &, boost::property_tree::ptree &);
bool upgrade_json (unsigned, boost::property_tree::ptree &);
nano::error serialize_json (nano::jsonconfig &) const;
nano::error deserialize_json (bool &, nano::jsonconfig &);
bool upgrade_json (unsigned, nano::jsonconfig &);
bool ledger_logging () const;
bool ledger_duplicate_logging () const;
bool vote_logging () const;
@ -58,6 +59,9 @@ public:
uintmax_t max_size;
uintmax_t rotation_size;
boost::log::sources::logger_mt log;
static constexpr int json_version = 5;
int json_version () const
{
return 5;
}
};
}

View file

@ -1,3 +1,4 @@
#include <nano/lib/jsonconfig.hpp>
#include <nano/node/nodeconfig.hpp>
// NOTE: to reduce compile times, this include can be replaced by more narrow includes
// once nano::network is factored out of node.{c|h}pp
@ -61,262 +62,270 @@ block_processor_batch_max_time (std::chrono::milliseconds (5000))
}
}
void nano::node_config::serialize_json (boost::property_tree::ptree & tree_a) const
nano::error nano::node_config::serialize_json (nano::jsonconfig & json) const
{
tree_a.put ("version", std::to_string (json_version));
tree_a.put ("peering_port", std::to_string (peering_port));
tree_a.put ("bootstrap_fraction_numerator", std::to_string (bootstrap_fraction_numerator));
tree_a.put ("receive_minimum", receive_minimum.to_string_dec ());
boost::property_tree::ptree logging_l;
json.put ("version", json_version ());
json.put ("peering_port", peering_port);
json.put ("bootstrap_fraction_numerator", bootstrap_fraction_numerator);
json.put ("receive_minimum", receive_minimum.to_string_dec ());
nano::jsonconfig logging_l;
logging.serialize_json (logging_l);
tree_a.add_child ("logging", logging_l);
boost::property_tree::ptree work_peers_l;
json.put_child ("logging", logging_l);
nano::jsonconfig work_peers_l;
for (auto i (work_peers.begin ()), n (work_peers.end ()); i != n; ++i)
{
boost::property_tree::ptree entry;
entry.put ("", boost::str (boost::format ("%1%:%2%") % i->first % i->second));
work_peers_l.push_back (std::make_pair ("", entry));
work_peers_l.push (boost::str (boost::format ("%1%:%2%") % i->first % i->second));
}
tree_a.add_child ("work_peers", work_peers_l);
boost::property_tree::ptree preconfigured_peers_l;
json.put_child ("work_peers", work_peers_l);
nano::jsonconfig preconfigured_peers_l;
for (auto i (preconfigured_peers.begin ()), n (preconfigured_peers.end ()); i != n; ++i)
{
boost::property_tree::ptree entry;
entry.put ("", *i);
preconfigured_peers_l.push_back (std::make_pair ("", entry));
preconfigured_peers_l.push (*i);
}
tree_a.add_child ("preconfigured_peers", preconfigured_peers_l);
boost::property_tree::ptree preconfigured_representatives_l;
json.put_child ("preconfigured_peers", preconfigured_peers_l);
nano::jsonconfig preconfigured_representatives_l;
for (auto i (preconfigured_representatives.begin ()), n (preconfigured_representatives.end ()); i != n; ++i)
{
boost::property_tree::ptree entry;
entry.put ("", i->to_account ());
preconfigured_representatives_l.push_back (std::make_pair ("", entry));
preconfigured_representatives_l.push (i->to_account ());
}
tree_a.add_child ("preconfigured_representatives", preconfigured_representatives_l);
tree_a.put ("online_weight_minimum", online_weight_minimum.to_string_dec ());
tree_a.put ("online_weight_quorum", std::to_string (online_weight_quorum));
tree_a.put ("password_fanout", std::to_string (password_fanout));
tree_a.put ("io_threads", std::to_string (io_threads));
tree_a.put ("network_threads", std::to_string (network_threads));
tree_a.put ("work_threads", std::to_string (work_threads));
tree_a.put ("enable_voting", enable_voting);
tree_a.put ("bootstrap_connections", bootstrap_connections);
tree_a.put ("bootstrap_connections_max", bootstrap_connections_max);
tree_a.put ("callback_address", callback_address);
tree_a.put ("callback_port", std::to_string (callback_port));
tree_a.put ("callback_target", callback_target);
tree_a.put ("lmdb_max_dbs", lmdb_max_dbs);
tree_a.put ("block_processor_batch_max_time", block_processor_batch_max_time.count ());
tree_a.put ("allow_local_peers", allow_local_peers);
json.put_child ("preconfigured_representatives", preconfigured_representatives_l);
json.put ("online_weight_minimum", online_weight_minimum.to_string_dec ());
json.put ("online_weight_quorum", online_weight_quorum);
json.put ("password_fanout", password_fanout);
json.put ("io_threads", io_threads);
json.put ("network_threads", network_threads);
json.put ("work_threads", work_threads);
json.put ("enable_voting", enable_voting);
json.put ("bootstrap_connections", bootstrap_connections);
json.put ("bootstrap_connections_max", bootstrap_connections_max);
json.put ("callback_address", callback_address);
json.put ("callback_port", callback_port);
json.put ("callback_target", callback_target);
json.put ("lmdb_max_dbs", lmdb_max_dbs);
json.put ("block_processor_batch_max_time", block_processor_batch_max_time.count ());
json.put ("allow_local_peers", allow_local_peers);
return json.get_error ();
}
bool nano::node_config::upgrade_json (unsigned version_a, boost::property_tree::ptree & tree_a)
bool nano::node_config::upgrade_json (unsigned version_a, nano::jsonconfig & json)
{
tree_a.put ("version", std::to_string (json_version));
auto result (false);
json.put ("version", json_version ());
auto upgraded (false);
switch (version_a)
{
case 1:
{
auto reps_l (tree_a.get_child ("preconfigured_representatives"));
boost::property_tree::ptree reps;
for (auto i (reps_l.begin ()), n (reps_l.end ()); i != n; ++i)
{
nano::jsonconfig reps_l;
json.get_required_child ("preconfigured_representatives", reps_l);
nano::jsonconfig reps;
reps_l.array_entries<std::string> ([&reps](std::string entry) {
nano::uint256_union account;
account.decode_account (i->second.get<std::string> (""));
boost::property_tree::ptree entry;
entry.put ("", account.to_account ());
reps.push_back (std::make_pair ("", entry));
}
tree_a.erase ("preconfigured_representatives");
tree_a.add_child ("preconfigured_representatives", reps);
result = true;
account.decode_account (entry);
reps.push (account.to_account ());
});
json.replace_child ("preconfigured_representatives", reps);
upgraded = true;
}
case 2:
{
tree_a.put ("inactive_supply", nano::uint128_union (0).to_string_dec ());
tree_a.put ("password_fanout", std::to_string (1024));
tree_a.put ("io_threads", std::to_string (io_threads));
tree_a.put ("work_threads", std::to_string (work_threads));
result = true;
json.put ("inactive_supply", nano::uint128_union (0).to_string_dec ());
json.put ("password_fanout", std::to_string (1024));
json.put ("io_threads", std::to_string (io_threads));
json.put ("work_threads", std::to_string (work_threads));
upgraded = true;
}
case 3:
tree_a.erase ("receive_minimum");
tree_a.put ("receive_minimum", nano::xrb_ratio.convert_to<std::string> ());
result = true;
json.erase ("receive_minimum");
json.put ("receive_minimum", nano::xrb_ratio.convert_to<std::string> ());
upgraded = true;
case 4:
tree_a.erase ("receive_minimum");
tree_a.put ("receive_minimum", nano::xrb_ratio.convert_to<std::string> ());
result = true;
json.erase ("receive_minimum");
json.put ("receive_minimum", nano::xrb_ratio.convert_to<std::string> ());
upgraded = true;
case 5:
tree_a.put ("enable_voting", enable_voting);
tree_a.erase ("packet_delay_microseconds");
tree_a.erase ("rebroadcast_delay");
tree_a.erase ("creation_rebroadcast");
result = true;
json.put ("enable_voting", enable_voting);
json.erase ("packet_delay_microseconds");
json.erase ("rebroadcast_delay");
json.erase ("creation_rebroadcast");
upgraded = true;
case 6:
tree_a.put ("bootstrap_connections", 16);
tree_a.put ("callback_address", "");
tree_a.put ("callback_port", "0");
tree_a.put ("callback_target", "");
result = true;
json.put ("bootstrap_connections", 16);
json.put ("callback_address", "");
json.put ("callback_port", 0);
json.put ("callback_target", "");
upgraded = true;
case 7:
tree_a.put ("lmdb_max_dbs", "128");
result = true;
json.put ("lmdb_max_dbs", 128);
upgraded = true;
case 8:
tree_a.put ("bootstrap_connections_max", "64");
result = true;
json.put ("bootstrap_connections_max", "64");
upgraded = true;
case 9:
tree_a.put ("state_block_parse_canary", nano::block_hash (0).to_string ());
tree_a.put ("state_block_generate_canary", nano::block_hash (0).to_string ());
result = true;
json.put ("state_block_parse_canary", nano::block_hash (0).to_string ());
json.put ("state_block_generate_canary", nano::block_hash (0).to_string ());
upgraded = true;
case 10:
tree_a.put ("online_weight_minimum", online_weight_minimum.to_string_dec ());
tree_a.put ("online_weight_quorom", std::to_string (online_weight_quorum));
tree_a.erase ("inactive_supply");
result = true;
json.put ("online_weight_minimum", online_weight_minimum.to_string_dec ());
json.put ("online_weight_quorom", std::to_string (online_weight_quorum));
json.erase ("inactive_supply");
upgraded = true;
case 11:
{
auto online_weight_quorum_l (tree_a.get<std::string> ("online_weight_quorom"));
tree_a.erase ("online_weight_quorom");
tree_a.put ("online_weight_quorum", online_weight_quorum_l);
result = true;
// Rename
std::string online_weight_quorum_l;
json.get<std::string> ("online_weight_quorom", online_weight_quorum_l);
json.erase ("online_weight_quorom");
json.put ("online_weight_quorum", online_weight_quorum_l);
upgraded = true;
}
case 12:
tree_a.erase ("state_block_parse_canary");
tree_a.erase ("state_block_generate_canary");
result = true;
json.erase ("state_block_parse_canary");
json.erase ("state_block_generate_canary");
upgraded = true;
case 13:
tree_a.put ("generate_hash_votes_at", "0");
result = true;
json.put ("generate_hash_votes_at", 0);
upgraded = true;
case 14:
tree_a.put ("network_threads", std::to_string (network_threads));
tree_a.erase ("generate_hash_votes_at");
tree_a.put ("block_processor_batch_max_time", block_processor_batch_max_time.count ());
result = true;
json.put ("network_threads", std::to_string (network_threads));
json.erase ("generate_hash_votes_at");
json.put ("block_processor_batch_max_time", block_processor_batch_max_time.count ());
upgraded = true;
case 15:
tree_a.put ("allow_local_peers", allow_local_peers);
result = true;
json.put ("allow_local_peers", allow_local_peers);
upgraded = true;
case 16:
break;
default:
throw std::runtime_error ("Unknown node_config version");
}
return result;
return upgraded;
}
bool nano::node_config::deserialize_json (bool & upgraded_a, boost::property_tree::ptree & tree_a)
nano::error nano::node_config::deserialize_json (bool & upgraded_a, nano::jsonconfig & json)
{
auto result (false);
try
{
auto version_l (tree_a.get_optional<std::string> ("version"));
auto version_l (json.get_optional<unsigned> ("version"));
if (!version_l)
{
tree_a.put ("version", "1");
version_l = "1";
auto work_peers_l (tree_a.get_child_optional ("work_peers"));
version_l = 1;
json.put ("version", version_l);
auto work_peers_l (json.get_optional_child ("work_peers"));
if (!work_peers_l)
{
tree_a.add_child ("work_peers", boost::property_tree::ptree ());
nano::jsonconfig empty;
json.put_child ("work_peers", empty);
}
upgraded_a = true;
}
upgraded_a |= upgrade_json (std::stoull (version_l.get ()), tree_a);
auto peering_port_l (tree_a.get<std::string> ("peering_port"));
auto bootstrap_fraction_numerator_l (tree_a.get<std::string> ("bootstrap_fraction_numerator"));
auto receive_minimum_l (tree_a.get<std::string> ("receive_minimum"));
auto & logging_l (tree_a.get_child ("logging"));
upgraded_a |= upgrade_json (version_l.get (), json);
auto logging_l (json.get_required_child ("logging"));
logging.deserialize_json (upgraded_a, logging_l);
work_peers.clear ();
auto work_peers_l (tree_a.get_child ("work_peers"));
for (auto i (work_peers_l.begin ()), n (work_peers_l.end ()); i != n; ++i)
{
auto work_peer (i->second.get<std::string> (""));
auto port_position (work_peer.rfind (':'));
result |= port_position == -1;
auto work_peers_l (json.get_required_child ("work_peers"));
work_peers_l.array_entries<std::string> ([this](std::string entry) {
auto port_position (entry.rfind (':'));
bool result = port_position == -1;
if (!result)
{
auto port_str (work_peer.substr (port_position + 1));
auto port_str (entry.substr (port_position + 1));
uint16_t port;
result |= parse_port (port_str, port);
if (!result)
{
auto address (work_peer.substr (0, port_position));
work_peers.push_back (std::make_pair (address, port));
auto address (entry.substr (0, port_position));
this->work_peers.push_back (std::make_pair (address, port));
}
}
}
auto preconfigured_peers_l (tree_a.get_child ("preconfigured_peers"));
});
nano::jsonconfig preconfigured_peers_l;
json.get_required_child ("preconfigured_peers", preconfigured_peers_l);
preconfigured_peers.clear ();
for (auto i (preconfigured_peers_l.begin ()), n (preconfigured_peers_l.end ()); i != n; ++i)
{
auto bootstrap_peer (i->second.get<std::string> (""));
preconfigured_peers.push_back (bootstrap_peer);
}
auto preconfigured_representatives_l (tree_a.get_child ("preconfigured_representatives"));
preconfigured_peers_l.array_entries<std::string> ([this](std::string entry) {
preconfigured_peers.push_back (entry);
});
nano::jsonconfig preconfigured_representatives_l;
json.get_required_child ("preconfigured_representatives", preconfigured_representatives_l);
preconfigured_representatives.clear ();
for (auto i (preconfigured_representatives_l.begin ()), n (preconfigured_representatives_l.end ()); i != n; ++i)
{
preconfigured_representatives_l.array_entries<std::string> ([this, &json](std::string entry) {
nano::account representative (0);
result = result || representative.decode_account (i->second.get<std::string> (""));
if (representative.decode_account (entry))
{
json.get_error ().set ("Invalid representative account: " + entry);
}
preconfigured_representatives.push_back (representative);
}
});
if (preconfigured_representatives.empty ())
{
result = true;
json.get_error ().set ("At least one representative account must be set");
}
auto stat_config_l (tree_a.get_child_optional ("statistics"));
auto stat_config_l (json.get_optional_child ("statistics"));
if (stat_config_l)
{
result |= stat_config.deserialize_json (stat_config_l.get ());
stat_config.deserialize_json (stat_config_l.get ());
}
auto online_weight_minimum_l (tree_a.get<std::string> ("online_weight_minimum"));
auto online_weight_quorum_l (tree_a.get<std::string> ("online_weight_quorum"));
auto password_fanout_l (tree_a.get<std::string> ("password_fanout"));
auto io_threads_l (tree_a.get<std::string> ("io_threads"));
auto work_threads_l (tree_a.get<std::string> ("work_threads"));
enable_voting = tree_a.get<bool> ("enable_voting");
auto bootstrap_connections_l (tree_a.get<std::string> ("bootstrap_connections"));
auto bootstrap_connections_max_l (tree_a.get<std::string> ("bootstrap_connections_max"));
callback_address = tree_a.get<std::string> ("callback_address");
auto callback_port_l (tree_a.get<std::string> ("callback_port"));
callback_target = tree_a.get<std::string> ("callback_target");
auto lmdb_max_dbs_l = tree_a.get<std::string> ("lmdb_max_dbs");
result |= parse_port (callback_port_l, callback_port);
auto block_processor_batch_max_time_l = tree_a.get<std::string> ("block_processor_batch_max_time");
try
auto receive_minimum_l (json.get<std::string> ("receive_minimum"));
if (receive_minimum.decode_dec (receive_minimum_l))
{
peering_port = std::stoul (peering_port_l);
bootstrap_fraction_numerator = std::stoul (bootstrap_fraction_numerator_l);
password_fanout = std::stoul (password_fanout_l);
io_threads = std::stoul (io_threads_l);
network_threads = tree_a.get<unsigned> ("network_threads", network_threads);
work_threads = std::stoul (work_threads_l);
bootstrap_connections = std::stoul (bootstrap_connections_l);
bootstrap_connections_max = std::stoul (bootstrap_connections_max_l);
lmdb_max_dbs = std::stoi (lmdb_max_dbs_l);
online_weight_quorum = std::stoul (online_weight_quorum_l);
block_processor_batch_max_time = std::chrono::milliseconds (std::stoul (block_processor_batch_max_time_l));
result |= peering_port > std::numeric_limits<uint16_t>::max ();
result |= logging.deserialize_json (upgraded_a, logging_l);
result |= receive_minimum.decode_dec (receive_minimum_l);
result |= online_weight_minimum.decode_dec (online_weight_minimum_l);
result |= online_weight_quorum > 100;
result |= password_fanout < 16;
result |= password_fanout > 1024 * 1024;
result |= io_threads == 0;
json.get_error ().set ("receive_minimum contains an invalid decimal amount");
}
catch (std::logic_error const &)
auto online_weight_minimum_l (json.get<std::string> ("online_weight_minimum"));
if (online_weight_minimum.decode_dec (online_weight_minimum_l))
{
result = true;
json.get_error ().set ("online_weight_minimum contains an invalid decimal amount");
}
auto block_processor_batch_max_time_l (json.get<unsigned long> ("block_processor_batch_max_time"));
block_processor_batch_max_time = std::chrono::milliseconds (block_processor_batch_max_time_l);
json.get<uint16_t> ("peering_port", peering_port);
json.get<unsigned> ("bootstrap_fraction_numerator", bootstrap_fraction_numerator);
json.get<unsigned> ("online_weight_quorum", online_weight_quorum);
json.get<unsigned> ("password_fanout", password_fanout);
json.get<unsigned> ("io_threads", io_threads);
json.get<unsigned> ("work_threads", work_threads);
json.get<unsigned> ("network_threads", network_threads);
json.get<unsigned> ("bootstrap_connections", bootstrap_connections);
json.get<unsigned> ("bootstrap_connections_max", bootstrap_connections_max);
json.get<std::string> ("callback_address", callback_address);
json.get<uint16_t> ("callback_port", callback_port);
json.get<std::string> ("callback_target", callback_target);
json.get<int> ("lmdb_max_dbs", lmdb_max_dbs);
json.get<bool> ("enable_voting", enable_voting);
// Validate ranges
if (online_weight_quorum > 100)
{
json.get_error ().set ("online_weight_quorum must be less than 100");
}
if (password_fanout < 16 || password_fanout > 1024 * 1024)
{
json.get_error ().set ("password_fanout must a number between 16 and 1048576");
}
if (io_threads == 0)
{
json.get_error ().set ("io_threads must be non-zero");
}
}
catch (std::runtime_error const &)
catch (std::runtime_error const & ex)
{
result = true;
json.get_error ().set (ex.what ());
}
return result;
return json.get_error ();
}
nano::account nano::node_config::random_representative ()

View file

@ -1,7 +1,8 @@
#pragma once
#include <boost/property_tree/ptree.hpp>
#include <chrono>
#include <nano/lib/errors.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/node/logging.hpp>
#include <nano/node/stats.hpp>
@ -17,9 +18,9 @@ class node_config
public:
node_config ();
node_config (uint16_t, nano::logging const &);
void serialize_json (boost::property_tree::ptree &) const;
bool deserialize_json (bool &, boost::property_tree::ptree &);
bool upgrade_json (unsigned, boost::property_tree::ptree &);
nano::error serialize_json (nano::jsonconfig &) const;
nano::error deserialize_json (bool &, nano::jsonconfig &);
bool upgrade_json (unsigned, nano::jsonconfig &);
nano::account random_representative ();
uint16_t peering_port;
nano::logging logging;
@ -49,7 +50,10 @@ public:
static std::chrono::seconds constexpr keepalive_period = std::chrono::seconds (60);
static std::chrono::seconds constexpr keepalive_cutoff = keepalive_period * 5;
static std::chrono::minutes constexpr wallet_backup_interval = std::chrono::minutes (5);
static constexpr int json_version = 16;
inline int json_version () const
{
return 16;
}
};
class node_flags

View file

@ -512,37 +512,20 @@ threads (threads_a)
{
}
void nano::opencl_config::serialize_json (boost::property_tree::ptree & tree_a) const
nano::error nano::opencl_config::serialize_json (nano::jsonconfig & json) const
{
tree_a.put ("platform", std::to_string (platform));
tree_a.put ("device", std::to_string (device));
tree_a.put ("threads", std::to_string (threads));
json.put ("platform", platform);
json.put ("device", device);
json.put ("threads", threads);
return json.get_error ();
}
bool nano::opencl_config::deserialize_json (boost::property_tree::ptree const & tree_a)
nano::error nano::opencl_config::deserialize_json (nano::jsonconfig & json)
{
auto result (false);
try
{
auto platform_l (tree_a.get<std::string> ("platform"));
auto device_l (tree_a.get<std::string> ("device"));
auto threads_l (tree_a.get<std::string> ("threads"));
try
{
platform = std::stoull (platform_l);
device = std::stoull (device_l);
threads = std::stoull (threads_l);
}
catch (std::logic_error const &)
{
result = true;
}
}
catch (std::runtime_error const &)
{
result = true;
}
return result;
json.get_optional<unsigned> ("platform", platform);
json.get_optional<unsigned> ("device", device);
json.get_optional<unsigned> ("threads", threads);
return json.get_error ();
}
nano::opencl_work::opencl_work (bool & error_a, nano::opencl_config const & config_a, nano::opencl_environment & environment_a, nano::logging & logging_a) :

View file

@ -1,9 +1,10 @@
#pragma once
#include <nano/node/xorshift.hpp>
#include <boost/optional.hpp>
#include <boost/property_tree/ptree.hpp>
#include <nano/lib/errors.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/node/xorshift.hpp>
#include <map>
#include <mutex>
@ -39,8 +40,8 @@ class opencl_config
public:
opencl_config ();
opencl_config (unsigned, unsigned, unsigned);
void serialize_json (boost::property_tree::ptree &) const;
bool deserialize_json (boost::property_tree::ptree const &);
nano::error serialize_json (nano::jsonconfig &) const;
nano::error deserialize_json (nano::jsonconfig &);
unsigned platform;
unsigned device;
unsigned threads;

View file

@ -1,8 +1,7 @@
#include <boost/algorithm/string.hpp>
#include <nano/node/rpc.hpp>
#include <nano/lib/interface.h>
#include <nano/node/node.hpp>
#include <nano/node/rpc.hpp>
#ifdef NANO_SECURE_RPC
#include <nano/node/rpc_secure.hpp>
@ -16,35 +15,28 @@ verbose_logging (false)
{
}
void nano::rpc_secure_config::serialize_json (boost::property_tree::ptree & tree_a) const
nano::error nano::rpc_secure_config::serialize_json (nano::jsonconfig & json) const
{
tree_a.put ("enable", enable);
tree_a.put ("verbose_logging", verbose_logging);
tree_a.put ("server_key_passphrase", server_key_passphrase);
tree_a.put ("server_cert_path", server_cert_path);
tree_a.put ("server_key_path", server_key_path);
tree_a.put ("server_dh_path", server_dh_path);
tree_a.put ("client_certs_path", client_certs_path);
json.put ("enable", enable);
json.put ("verbose_logging", verbose_logging);
json.put ("server_key_passphrase", server_key_passphrase);
json.put ("server_cert_path", server_cert_path);
json.put ("server_key_path", server_key_path);
json.put ("server_dh_path", server_dh_path);
json.put ("client_certs_path", client_certs_path);
return json.get_error ();
}
bool nano::rpc_secure_config::deserialize_json (boost::property_tree::ptree const & tree_a)
nano::error nano::rpc_secure_config::deserialize_json (nano::jsonconfig & json)
{
auto error (false);
try
{
enable = tree_a.get<bool> ("enable");
verbose_logging = tree_a.get<bool> ("verbose_logging");
server_key_passphrase = tree_a.get<std::string> ("server_key_passphrase");
server_cert_path = tree_a.get<std::string> ("server_cert_path");
server_key_path = tree_a.get<std::string> ("server_key_path");
server_dh_path = tree_a.get<std::string> ("server_dh_path");
client_certs_path = tree_a.get<std::string> ("client_certs_path");
}
catch (std::runtime_error const &)
{
error = true;
}
return error;
json.get_required<bool> ("enable", enable);
json.get_required<bool> ("verbose_logging", verbose_logging);
json.get_required<std::string> ("server_key_passphrase", server_key_passphrase);
json.get_required<std::string> ("server_cert_path", server_cert_path);
json.get_required<std::string> ("server_key_path", server_key_path);
json.get_required<std::string> ("server_dh_path", server_dh_path);
json.get_required<std::string> ("client_certs_path", client_certs_path);
return json.get_error ();
}
nano::rpc_config::rpc_config () :
@ -67,59 +59,33 @@ max_json_depth (20)
{
}
void nano::rpc_config::serialize_json (boost::property_tree::ptree & tree_a) const
nano::error nano::rpc_config::serialize_json (nano::jsonconfig & json) const
{
tree_a.put ("address", address.to_string ());
tree_a.put ("port", std::to_string (port));
tree_a.put ("enable_control", enable_control);
tree_a.put ("frontier_request_limit", frontier_request_limit);
tree_a.put ("chain_request_limit", chain_request_limit);
tree_a.put ("max_json_depth", max_json_depth);
json.put ("address", address.to_string ());
json.put ("port", port);
json.put ("enable_control", enable_control);
json.put ("frontier_request_limit", frontier_request_limit);
json.put ("chain_request_limit", chain_request_limit);
json.put ("max_json_depth", max_json_depth);
return json.get_error ();
}
bool nano::rpc_config::deserialize_json (boost::property_tree::ptree const & tree_a)
nano::error nano::rpc_config::deserialize_json (nano::jsonconfig & json)
{
auto result (false);
try
boost::optional<nano::jsonconfig> rpc_secure_l;
json.get_optional_child ("secure", rpc_secure_l);
if (rpc_secure_l)
{
auto rpc_secure_l (tree_a.get_child_optional ("secure"));
if (rpc_secure_l)
{
result = secure.deserialize_json (rpc_secure_l.get ());
}
secure.deserialize_json (*rpc_secure_l);
}
if (!result)
{
auto address_l (tree_a.get<std::string> ("address"));
auto port_l (tree_a.get<std::string> ("port"));
enable_control = tree_a.get<bool> ("enable_control");
auto frontier_request_limit_l (tree_a.get<std::string> ("frontier_request_limit"));
auto chain_request_limit_l (tree_a.get<std::string> ("chain_request_limit"));
max_json_depth = tree_a.get<uint8_t> ("max_json_depth", max_json_depth);
try
{
port = std::stoul (port_l);
result = port > std::numeric_limits<uint16_t>::max ();
frontier_request_limit = std::stoull (frontier_request_limit_l);
chain_request_limit = std::stoull (chain_request_limit_l);
}
catch (std::logic_error const &)
{
result = true;
}
boost::system::error_code ec;
address = boost::asio::ip::address_v6::from_string (address_l, ec);
if (ec)
{
result = true;
}
}
}
catch (std::runtime_error const &)
{
result = true;
}
return result;
json.get_required<boost::asio::ip::address_v6> ("address", address);
json.get_optional<uint16_t> ("port", port);
json.get_optional<bool> ("enable_control", enable_control);
json.get_optional<uint64_t> ("frontier_request_limit", frontier_request_limit);
json.get_optional<uint64_t> ("chain_request_limit", chain_request_limit);
json.get_optional<uint8_t> ("max_json_depth", max_json_depth);
return json.get_error ();
}
nano::rpc::rpc (boost::asio::io_context & io_ctx_a, nano::node & node_a, nano::rpc_config const & config_a) :

View file

@ -5,6 +5,8 @@
#include <boost/beast.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <boost/property_tree/ptree.hpp>
#include <nano/lib/errors.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/secure/utility.hpp>
#include <unordered_map>
@ -17,8 +19,8 @@ class rpc_secure_config
{
public:
rpc_secure_config ();
void serialize_json (boost::property_tree::ptree &) const;
bool deserialize_json (boost::property_tree::ptree const &);
nano::error serialize_json (nano::jsonconfig &) const;
nano::error deserialize_json (nano::jsonconfig &);
/** If true, enable TLS */
bool enable;
@ -40,8 +42,8 @@ class rpc_config
public:
rpc_config ();
rpc_config (bool);
void serialize_json (boost::property_tree::ptree &) const;
bool deserialize_json (boost::property_tree::ptree const &);
nano::error serialize_json (nano::jsonconfig &) const;
nano::error deserialize_json (nano::jsonconfig &);
boost::asio::ip::address_v6 address;
uint16_t port;
bool enable_control;

View file

@ -9,33 +9,34 @@
#include <sstream>
#include <tuple>
bool nano::stat_config::deserialize_json (boost::property_tree::ptree & tree_a)
nano::error nano::stat_config::deserialize_json (nano::jsonconfig & json)
{
bool error = false;
auto sampling_l (tree_a.get_child_optional ("sampling"));
auto sampling_l (json.get_optional_child ("sampling"));
if (sampling_l)
{
sampling_enabled = sampling_l->get<bool> ("enabled", sampling_enabled);
capacity = sampling_l->get<size_t> ("capacity", capacity);
interval = sampling_l->get<size_t> ("interval", interval);
sampling_l->get<bool> ("enabled", sampling_enabled);
sampling_l->get<size_t> ("capacity", capacity);
sampling_l->get<size_t> ("interval", interval);
}
auto log_l (tree_a.get_child_optional ("log"));
auto log_l (json.get_optional_child ("log"));
if (log_l)
{
log_headers = log_l->get<bool> ("headers", log_headers);
log_interval_counters = log_l->get<size_t> ("interval_counters", log_interval_counters);
log_interval_samples = log_l->get<size_t> ("interval_samples", log_interval_samples);
log_rotation_count = log_l->get<size_t> ("rotation_count", log_rotation_count);
log_counters_filename = log_l->get<std::string> ("filename_counters", log_counters_filename);
log_samples_filename = log_l->get<std::string> ("filename_samples", log_samples_filename);
log_l->get<bool> ("headers", log_headers);
log_l->get<size_t> ("interval_counters", log_interval_counters);
log_l->get<size_t> ("interval_samples", log_interval_samples);
log_l->get<size_t> ("rotation_count", log_rotation_count);
log_l->get<std::string> ("filename_counters", log_counters_filename);
log_l->get<std::string> ("filename_samples", log_samples_filename);
// Don't allow specifying the same file name for counter and samples logs
error = (log_counters_filename == log_samples_filename);
if (log_counters_filename == log_samples_filename)
{
json.get_error ().set ("The statistics counter and samples config values must be different");
}
}
return error;
return json.get_error ();
}
std::string nano::stat_log_sink::tm_to_string (tm & tm)

View file

@ -6,6 +6,8 @@
#include <chrono>
#include <map>
#include <memory>
#include <nano/lib/errors.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/lib/utility.hpp>
#include <string>
#include <unordered_map>
@ -23,7 +25,7 @@ class stat_config
{
public:
/** Reads the JSON statistics node */
bool deserialize_json (boost::property_tree::ptree & tree_a);
nano::error deserialize_json (nano::jsonconfig & json);
/** If true, sampling of counters is enabled */
bool sampling_enabled{ false };

View file

@ -109,14 +109,3 @@ std::vector<boost::filesystem::path> nano::remove_temporary_directories ()
}
return all_unique_paths;
}
void nano::open_or_create (std::fstream & stream_a, std::string const & path_a)
{
stream_a.open (path_a, std::ios_base::in);
if (stream_a.fail ())
{
stream_a.open (path_a, std::ios_base::out);
}
stream_a.close ();
stream_a.open (path_a, std::ios_base::in | std::ios_base::out);
}

View file

@ -13,6 +13,7 @@
#include <cryptopp/osrng.h>
#include <nano/lib/config.hpp>
#include <nano/lib/errors.hpp>
#include <nano/lib/interface.h>
#include <nano/lib/numbers.hpp>
@ -29,81 +30,4 @@ bool migrate_working_path (std::string &);
boost::filesystem::path unique_path ();
// Remove all unique tmp directories created by the process. The list of unique paths are returned.
std::vector<boost::filesystem::path> remove_temporary_directories ();
// C++ stream are absolutely horrible so I need this helper function to do the most basic operation of creating a file if it doesn't exist or truncating it.
void open_or_create (std::fstream &, std::string const &);
// Reads a json object from the stream and if was changed, write the object back to the stream
template <typename T>
bool fetch_object (T & object, std::iostream & stream_a)
{
assert (stream_a.tellg () == std::streampos (0) || stream_a.tellg () == std::streampos (-1));
assert (stream_a.tellp () == std::streampos (0) || stream_a.tellp () == std::streampos (-1));
bool error (false);
boost::property_tree::ptree tree;
try
{
boost::property_tree::read_json (stream_a, tree);
}
catch (std::runtime_error const &)
{
auto pos (stream_a.tellg ());
if (pos != std::streampos (0))
{
error = true;
}
}
if (!error)
{
auto updated (false);
error = object.deserialize_json (updated, tree);
}
return error;
}
// Reads a json object from the stream and if was changed, write the object back to the stream
template <typename T>
bool fetch_object (T & object, boost::filesystem::path const & path_a)
{
bool error (false);
std::fstream config_file;
nano::open_or_create (config_file, path_a.string ());
if (!config_file.fail ())
{
boost::property_tree::ptree tree;
try
{
boost::property_tree::read_json (config_file, tree);
}
catch (std::runtime_error const &)
{
auto pos (config_file.tellg ());
if (pos != std::streampos (0))
{
error = true;
}
}
config_file.close ();
if (!error)
{
auto updated (false);
error = object.deserialize_json (updated, tree);
if (!error && updated)
{
config_file.open (path_a.string (), std::ios_base::out | std::ios_base::trunc);
try
{
boost::property_tree::write_json (config_file, tree);
}
catch (std::runtime_error const &)
{
error = true;
}
config_file.close ();
}
}
}
return error;
}
}