TOML config file support and migration (#2221)
* TOML config file support and migration * Add config override support via a CLI option * Incorporate config changes from #2214 * Remove deprecation as this currently fails to compile on CI * Add default empty vector to read_node_config_toml * Make sure override streams are not empty * Adjust unit tests with changed base/override order * Incorporate config changes from #2198 and reorder some methods * httpcallback section, and documentation improvements * Clarify data type for wallet account, formatting * CI formatting * Improved node config unit test coverage, address review feedback * Full node and rpc config file coverage. Fix httpcallback deserialization. * Improve toml diff test * Use const ref in toml config, fix schema type for rpc_path * Incorporate work watcher period from #2222 * Add work_watcher_period validation as well as unit test
This commit is contained in:
parent
f8c637fc8d
commit
20b52accf1
39 changed files with 2214 additions and 365 deletions
3
.gitmodules
vendored
3
.gitmodules
vendored
|
@ -15,3 +15,6 @@
|
|||
path = gtest
|
||||
url = https://github.com/google/googletest.git
|
||||
branch = v1.8.x
|
||||
[submodule "cpptoml"]
|
||||
path = cpptoml
|
||||
url = https://github.com/cryptocode/cpptoml.git
|
||||
|
|
|
@ -193,6 +193,7 @@ else ()
|
|||
set (BOOST_PROCESS_SUPPORTED 1)
|
||||
endif ()
|
||||
|
||||
include_directories(cpptoml/include)
|
||||
add_subdirectory(crypto/ed25519-donna)
|
||||
|
||||
set (UPNPC_BUILD_SHARED OFF CACHE BOOL "")
|
||||
|
|
1
cpptoml
Submodule
1
cpptoml
Submodule
|
@ -0,0 +1 @@
|
|||
Subproject commit 539965005606a481ae29b723e21aa254d3c29c62
|
|
@ -26,6 +26,7 @@ add_executable (core_test
|
|||
${rocksdb_test}
|
||||
signing.cpp
|
||||
socket.cpp
|
||||
toml.cpp
|
||||
timer.cpp
|
||||
uint256_union.cpp
|
||||
utility.cpp
|
||||
|
|
487
nano/core_test/toml.cpp
Normal file
487
nano/core_test/toml.cpp
Normal file
|
@ -0,0 +1,487 @@
|
|||
#include <nano/core_test/testutil.hpp>
|
||||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/lib/rpcconfig.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
#include <nano/node/daemonconfig.hpp>
|
||||
#include <nano/node/testing.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include <numeric>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
|
||||
using namespace std::chrono_literals;
|
||||
|
||||
/** Ensure only different values survive a toml diff */
|
||||
TEST (toml, diff)
|
||||
{
|
||||
nano::tomlconfig defaults, other;
|
||||
|
||||
// Defaults
|
||||
std::stringstream ss;
|
||||
ss << R"toml(
|
||||
a = false
|
||||
b = false
|
||||
)toml";
|
||||
|
||||
defaults.read (ss);
|
||||
|
||||
// User file. The rpc section is the same and doesn't need to be emitted
|
||||
std::stringstream ss_override;
|
||||
ss_override << R"toml(
|
||||
a = true
|
||||
b = false
|
||||
)toml";
|
||||
|
||||
other.read (ss_override);
|
||||
other.erase_default_values (defaults);
|
||||
|
||||
ASSERT_TRUE (other.has_key ("a"));
|
||||
ASSERT_FALSE (other.has_key ("b"));
|
||||
}
|
||||
|
||||
/** Diff on equal toml files leads to an empty result */
|
||||
TEST (toml, diff_equal)
|
||||
{
|
||||
nano::tomlconfig defaults, other;
|
||||
|
||||
std::stringstream ss;
|
||||
ss << R"toml(
|
||||
[node]
|
||||
allow_local_peers = false
|
||||
)toml";
|
||||
|
||||
defaults.read (ss);
|
||||
|
||||
std::stringstream ss_override;
|
||||
ss_override << R"toml(
|
||||
[node]
|
||||
allow_local_peers = false
|
||||
)toml";
|
||||
|
||||
other.read (ss_override);
|
||||
other.erase_default_values (defaults);
|
||||
ASSERT_TRUE (other.empty ());
|
||||
}
|
||||
|
||||
TEST (toml, daemon_config_update_array)
|
||||
{
|
||||
nano::tomlconfig t;
|
||||
boost::filesystem::path data_path (".");
|
||||
nano::daemon_config c (data_path);
|
||||
c.node.preconfigured_peers.push_back ("test-peer.org");
|
||||
c.serialize_toml (t);
|
||||
c.deserialize_toml (t);
|
||||
ASSERT_EQ (c.node.preconfigured_peers[0], "test-peer.org");
|
||||
}
|
||||
|
||||
/** Empty config file should match a default config object */
|
||||
TEST (toml, daemon_config_deserialize_defaults)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << R"toml(
|
||||
)toml";
|
||||
|
||||
nano::tomlconfig t;
|
||||
t.read (ss);
|
||||
nano::daemon_config c;
|
||||
nano::daemon_config defaults;
|
||||
c.deserialize_toml (t);
|
||||
ASSERT_EQ (c.opencl_enable, defaults.opencl_enable);
|
||||
ASSERT_EQ (c.opencl.device, defaults.opencl.device);
|
||||
ASSERT_EQ (c.opencl.platform, defaults.opencl.platform);
|
||||
ASSERT_EQ (c.opencl.threads, defaults.opencl.threads);
|
||||
ASSERT_EQ (c.rpc.enable_sign_hash, false);
|
||||
ASSERT_EQ (c.rpc.max_work_generate_difficulty, 0xffffffffc0000000);
|
||||
ASSERT_EQ (c.rpc.child_process.enable, false);
|
||||
}
|
||||
|
||||
TEST (toml, optional_child)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << R"toml(
|
||||
[child]
|
||||
val=1
|
||||
)toml";
|
||||
|
||||
nano::tomlconfig t;
|
||||
t.read (ss);
|
||||
auto c1 = t.get_required_child ("child");
|
||||
int val = 0;
|
||||
c1.get_required ("val", val);
|
||||
ASSERT_EQ (val, 1);
|
||||
auto c2 = t.get_optional_child ("child2");
|
||||
ASSERT_FALSE (c2);
|
||||
}
|
||||
|
||||
/** Config settings passed via CLI overrides the config file settings. This is solved
|
||||
using an override stream. */
|
||||
TEST (toml, dot_child_syntax)
|
||||
{
|
||||
std::stringstream ss_override;
|
||||
ss_override << R"toml(
|
||||
node.a = 1
|
||||
node.b = 2
|
||||
)toml";
|
||||
|
||||
std::stringstream ss;
|
||||
ss << R"toml(
|
||||
[node]
|
||||
b=5
|
||||
c=3
|
||||
)toml";
|
||||
|
||||
nano::tomlconfig t;
|
||||
t.read (ss_override, ss);
|
||||
|
||||
auto node = t.get_required_child ("node");
|
||||
uint16_t a, b, c;
|
||||
node.get<uint16_t> ("a", a);
|
||||
ASSERT_EQ (a, 1);
|
||||
node.get<uint16_t> ("b", b);
|
||||
ASSERT_EQ (b, 2);
|
||||
node.get<uint16_t> ("c", c);
|
||||
ASSERT_EQ (c, 3);
|
||||
}
|
||||
|
||||
TEST (toml, base_override)
|
||||
{
|
||||
std::stringstream ss_base;
|
||||
ss_base << R"toml(
|
||||
node.peering_port=7075
|
||||
)toml";
|
||||
|
||||
std::stringstream ss_override;
|
||||
ss_override << R"toml(
|
||||
node.peering_port=8075
|
||||
node.too_big=70000
|
||||
)toml";
|
||||
|
||||
nano::tomlconfig t;
|
||||
t.read (ss_override, ss_base);
|
||||
|
||||
// Query optional existent value
|
||||
uint16_t port = 0;
|
||||
t.get_optional<uint16_t> ("node.peering_port", port);
|
||||
ASSERT_EQ (port, 8075);
|
||||
ASSERT_FALSE (t.get_error ());
|
||||
|
||||
// Query optional non-existent value, make sure we get default and no errors
|
||||
port = 65535;
|
||||
t.get_optional<uint16_t> ("node.peering_port_non_existent", port);
|
||||
ASSERT_EQ (port, 65535);
|
||||
ASSERT_FALSE (t.get_error ());
|
||||
t.get_error ().clear ();
|
||||
|
||||
// Query required non-existent value, make sure it errors
|
||||
t.get_required<uint16_t> ("node.peering_port_not_existent", port);
|
||||
ASSERT_EQ (port, 65535);
|
||||
ASSERT_TRUE (t.get_error ());
|
||||
ASSERT_TRUE (t.get_error () == nano::error_config::missing_value);
|
||||
t.get_error ().clear ();
|
||||
|
||||
// Query uint16 that's too big, make sure we have an error
|
||||
t.get_required<uint16_t> ("node.too_big", port);
|
||||
ASSERT_TRUE (t.get_error ());
|
||||
ASSERT_TRUE (t.get_error () == nano::error_config::invalid_value);
|
||||
}
|
||||
|
||||
TEST (toml, put)
|
||||
{
|
||||
nano::tomlconfig config;
|
||||
nano::tomlconfig config_node;
|
||||
// Overwrite value and add to child node
|
||||
config_node.put ("port", "7074");
|
||||
config_node.put ("port", "7075");
|
||||
config.put_child ("node", config_node);
|
||||
uint16_t port;
|
||||
config.get_required<uint16_t> ("node.port", port);
|
||||
ASSERT_EQ (port, 7075);
|
||||
ASSERT_FALSE (config.get_error ());
|
||||
}
|
||||
|
||||
TEST (toml, array)
|
||||
{
|
||||
nano::tomlconfig config;
|
||||
nano::tomlconfig config_node;
|
||||
config.put_child ("node", config_node);
|
||||
config_node.push<std::string> ("items", "item 1");
|
||||
config_node.push<std::string> ("items", "item 2");
|
||||
int i = 1;
|
||||
config_node.array_entries_required<std::string> ("items", [&i](std::string item) {
|
||||
ASSERT_TRUE (item == std::string ("item ") + std::to_string (i));
|
||||
i++;
|
||||
});
|
||||
}
|
||||
|
||||
/** Deserialize a node config with non-default values */
|
||||
TEST (toml, daemon_config_deserialize_no_defaults)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
// A config file with values that differs from test-net defaults
|
||||
ss << R"toml(
|
||||
[node]
|
||||
active_elections_size = 999
|
||||
allow_local_peers = false
|
||||
backup_before_upgrade = true
|
||||
bandwidth_limit = 999
|
||||
block_processor_batch_max_time = 999
|
||||
bootstrap_connections = 999
|
||||
bootstrap_connections_max = 999
|
||||
bootstrap_fraction_numerator = 999
|
||||
confirmation_history_size = 999
|
||||
enable_voting = false
|
||||
external_address = "0:0:0:0:0:ffff:7f01:101"
|
||||
external_port = 999
|
||||
io_threads = 999
|
||||
lmdb_max_dbs = 999
|
||||
network_threads = 999
|
||||
online_weight_minimum = "999"
|
||||
online_weight_quorum = 99
|
||||
password_fanout = 999
|
||||
peering_port = 999
|
||||
pow_sleep_interval= 999
|
||||
preconfigured_peers = ["test.org"]
|
||||
preconfigured_representatives = ["nano_3arg3asgtigae3xckabaaewkx3bzsh7nwz7jkmjos79ihyaxwphhm6qgjps4"]
|
||||
receive_minimum = "999"
|
||||
signature_checker_threads = 999
|
||||
tcp_incoming_connections_max = 999
|
||||
tcp_io_timeout = 999
|
||||
unchecked_cutoff_time = 999
|
||||
use_memory_pools = false
|
||||
vote_generator_delay = 999
|
||||
vote_generator_threshold = 9
|
||||
vote_minimum = "999"
|
||||
work_peers = ["test.org:999"]
|
||||
work_threads = 999
|
||||
work_watcher_period = 999
|
||||
[node.diagnostics.txn_tracking]
|
||||
enable = true
|
||||
ignore_writes_below_block_processor_max_time = false
|
||||
min_read_txn_time = 999
|
||||
min_write_txn_time = 999
|
||||
|
||||
[node.httpcallback]
|
||||
address = "test.org"
|
||||
port = 999
|
||||
target = "/test"
|
||||
|
||||
[node.ipc.local]
|
||||
allow_unsafe = true
|
||||
enable = true
|
||||
io_timeout = 999
|
||||
path = "/tmp/test"
|
||||
|
||||
[node.ipc.tcp]
|
||||
enable = true
|
||||
io_timeout = 999
|
||||
port = 999
|
||||
|
||||
[node.logging]
|
||||
bulk_pull = true
|
||||
flush = false
|
||||
insufficient_work = false
|
||||
ledger = true
|
||||
ledger_duplicate = true
|
||||
log_ipc = false
|
||||
log_to_cerr = true
|
||||
max_size = 999
|
||||
min_time_between_output = 999
|
||||
network = false
|
||||
network_keepalive = true
|
||||
network_message = true
|
||||
network_node_id_handshake = true
|
||||
network_packet = true
|
||||
network_publish = true
|
||||
network_timeout = true
|
||||
node_lifetime_tracing = true
|
||||
rotation_size = 999
|
||||
single_line_record = true
|
||||
timing = true
|
||||
upnp_details = true
|
||||
vote = true
|
||||
work_generation_time = false
|
||||
|
||||
[node.statistics.log]
|
||||
filename_counters = "testcounters.stat"
|
||||
filename_samples = "testsamples.stat"
|
||||
headers = false
|
||||
interval_counters = 999
|
||||
interval_samples = 999
|
||||
rotation_count = 999
|
||||
|
||||
[node.statistics.sampling]
|
||||
capacity = 999
|
||||
enable = true
|
||||
interval = 999
|
||||
|
||||
[node.websocket]
|
||||
address = "0:0:0:0:0:ffff:7f01:101"
|
||||
enable = true
|
||||
port = 999
|
||||
|
||||
[opencl]
|
||||
device = 999
|
||||
enable = true
|
||||
platform = 999
|
||||
threads = 999
|
||||
|
||||
[rpc]
|
||||
enable = true
|
||||
enable_sign_hash = true
|
||||
max_work_generate_difficulty = "ffffffffc9999999"
|
||||
|
||||
[rpc.child_process]
|
||||
enable = true
|
||||
rpc_path = "/test/nano_rpc"
|
||||
)toml";
|
||||
|
||||
nano::tomlconfig toml;
|
||||
toml.read (ss);
|
||||
nano::daemon_config conf;
|
||||
nano::daemon_config defaults;
|
||||
conf.deserialize_toml (toml);
|
||||
|
||||
ASSERT_FALSE (toml.get_error ()) << toml.get_error ().get_message ();
|
||||
|
||||
ASSERT_NE (conf.opencl_enable, defaults.opencl_enable);
|
||||
ASSERT_NE (conf.opencl.device, defaults.opencl.device);
|
||||
ASSERT_NE (conf.opencl.platform, defaults.opencl.platform);
|
||||
ASSERT_NE (conf.opencl.threads, defaults.opencl.threads);
|
||||
ASSERT_NE (conf.rpc_enable, defaults.rpc_enable);
|
||||
ASSERT_NE (conf.rpc.enable_sign_hash, defaults.rpc.enable_sign_hash);
|
||||
ASSERT_NE (conf.rpc.max_work_generate_difficulty, defaults.rpc.max_work_generate_difficulty);
|
||||
ASSERT_NE (conf.rpc.child_process.enable, defaults.rpc.child_process.enable);
|
||||
ASSERT_NE (conf.rpc.child_process.rpc_path, defaults.rpc.child_process.rpc_path);
|
||||
|
||||
ASSERT_NE (conf.node.active_elections_size, defaults.node.active_elections_size);
|
||||
ASSERT_NE (conf.node.allow_local_peers, defaults.node.allow_local_peers);
|
||||
ASSERT_NE (conf.node.backup_before_upgrade, defaults.node.backup_before_upgrade);
|
||||
ASSERT_NE (conf.node.bandwidth_limit, defaults.node.bandwidth_limit);
|
||||
ASSERT_NE (conf.node.block_processor_batch_max_time, defaults.node.block_processor_batch_max_time);
|
||||
ASSERT_NE (conf.node.bootstrap_connections, defaults.node.bootstrap_connections);
|
||||
ASSERT_NE (conf.node.bootstrap_connections_max, defaults.node.bootstrap_connections_max);
|
||||
ASSERT_NE (conf.node.bootstrap_fraction_numerator, defaults.node.bootstrap_fraction_numerator);
|
||||
ASSERT_NE (conf.node.confirmation_history_size, defaults.node.confirmation_history_size);
|
||||
ASSERT_NE (conf.node.enable_voting, defaults.node.enable_voting);
|
||||
ASSERT_NE (conf.node.external_address, defaults.node.external_address);
|
||||
ASSERT_NE (conf.node.external_port, defaults.node.external_port);
|
||||
ASSERT_NE (conf.node.io_threads, defaults.node.io_threads);
|
||||
ASSERT_NE (conf.node.lmdb_max_dbs, defaults.node.lmdb_max_dbs);
|
||||
ASSERT_NE (conf.node.network_threads, defaults.node.network_threads);
|
||||
ASSERT_NE (conf.node.work_watcher_period, defaults.node.work_watcher_period);
|
||||
ASSERT_NE (conf.node.online_weight_minimum, defaults.node.online_weight_minimum);
|
||||
ASSERT_NE (conf.node.online_weight_quorum, defaults.node.online_weight_quorum);
|
||||
ASSERT_NE (conf.node.password_fanout, defaults.node.password_fanout);
|
||||
ASSERT_NE (conf.node.peering_port, defaults.node.peering_port);
|
||||
ASSERT_NE (conf.node.pow_sleep_interval, defaults.node.pow_sleep_interval);
|
||||
ASSERT_NE (conf.node.preconfigured_peers, defaults.node.preconfigured_peers);
|
||||
ASSERT_NE (conf.node.preconfigured_representatives, defaults.node.preconfigured_representatives);
|
||||
ASSERT_NE (conf.node.receive_minimum, defaults.node.receive_minimum);
|
||||
ASSERT_NE (conf.node.signature_checker_threads, defaults.node.signature_checker_threads);
|
||||
ASSERT_NE (conf.node.tcp_incoming_connections_max, defaults.node.tcp_incoming_connections_max);
|
||||
ASSERT_NE (conf.node.tcp_io_timeout, defaults.node.tcp_io_timeout);
|
||||
ASSERT_NE (conf.node.unchecked_cutoff_time, defaults.node.unchecked_cutoff_time);
|
||||
ASSERT_NE (conf.node.use_memory_pools, defaults.node.use_memory_pools);
|
||||
ASSERT_NE (conf.node.vote_generator_delay, defaults.node.vote_generator_delay);
|
||||
ASSERT_NE (conf.node.vote_generator_threshold, defaults.node.vote_generator_threshold);
|
||||
ASSERT_NE (conf.node.vote_minimum, defaults.node.vote_minimum);
|
||||
ASSERT_NE (conf.node.work_peers, defaults.node.work_peers);
|
||||
ASSERT_NE (conf.node.work_threads, defaults.node.work_threads);
|
||||
|
||||
ASSERT_NE (conf.node.logging.bulk_pull_logging_value, defaults.node.logging.bulk_pull_logging_value);
|
||||
ASSERT_NE (conf.node.logging.flush, defaults.node.logging.flush);
|
||||
ASSERT_NE (conf.node.logging.insufficient_work_logging_value, defaults.node.logging.insufficient_work_logging_value);
|
||||
ASSERT_NE (conf.node.logging.ledger_logging_value, defaults.node.logging.ledger_logging_value);
|
||||
ASSERT_NE (conf.node.logging.ledger_duplicate_logging_value, defaults.node.logging.ledger_duplicate_logging_value);
|
||||
ASSERT_NE (conf.node.logging.log_ipc_value, defaults.node.logging.log_ipc_value);
|
||||
ASSERT_NE (conf.node.logging.log_to_cerr_value, defaults.node.logging.log_to_cerr_value);
|
||||
ASSERT_NE (conf.node.logging.max_size, defaults.node.logging.max_size);
|
||||
ASSERT_NE (conf.node.logging.min_time_between_log_output.count (), defaults.node.logging.min_time_between_log_output.count ());
|
||||
ASSERT_NE (conf.node.logging.network_logging_value, defaults.node.logging.network_logging_value);
|
||||
ASSERT_NE (conf.node.logging.network_keepalive_logging_value, defaults.node.logging.network_keepalive_logging_value);
|
||||
ASSERT_NE (conf.node.logging.network_message_logging_value, defaults.node.logging.network_message_logging_value);
|
||||
ASSERT_NE (conf.node.logging.network_node_id_handshake_logging_value, defaults.node.logging.network_node_id_handshake_logging_value);
|
||||
ASSERT_NE (conf.node.logging.network_packet_logging_value, defaults.node.logging.network_packet_logging_value);
|
||||
ASSERT_NE (conf.node.logging.network_publish_logging_value, defaults.node.logging.network_publish_logging_value);
|
||||
ASSERT_NE (conf.node.logging.network_timeout_logging_value, defaults.node.logging.network_timeout_logging_value);
|
||||
ASSERT_NE (conf.node.logging.node_lifetime_tracing_value, defaults.node.logging.node_lifetime_tracing_value);
|
||||
ASSERT_NE (conf.node.logging.rotation_size, defaults.node.logging.rotation_size);
|
||||
ASSERT_NE (conf.node.logging.single_line_record_value, defaults.node.logging.single_line_record_value);
|
||||
ASSERT_NE (conf.node.logging.timing_logging_value, defaults.node.logging.timing_logging_value);
|
||||
ASSERT_NE (conf.node.logging.upnp_details_logging_value, defaults.node.logging.upnp_details_logging_value);
|
||||
ASSERT_NE (conf.node.logging.vote_logging_value, defaults.node.logging.vote_logging_value);
|
||||
ASSERT_NE (conf.node.logging.work_generation_time_value, defaults.node.logging.work_generation_time_value);
|
||||
|
||||
ASSERT_NE (conf.node.websocket_config.enabled, defaults.node.websocket_config.enabled);
|
||||
ASSERT_NE (conf.node.websocket_config.address, defaults.node.websocket_config.address);
|
||||
ASSERT_NE (conf.node.websocket_config.port, defaults.node.websocket_config.port);
|
||||
|
||||
ASSERT_NE (conf.node.callback_address, defaults.node.callback_address);
|
||||
ASSERT_NE (conf.node.callback_port, defaults.node.callback_port);
|
||||
ASSERT_NE (conf.node.callback_target, defaults.node.callback_target);
|
||||
|
||||
ASSERT_NE (conf.node.ipc_config.transport_domain.allow_unsafe, defaults.node.ipc_config.transport_domain.allow_unsafe);
|
||||
ASSERT_NE (conf.node.ipc_config.transport_domain.enabled, defaults.node.ipc_config.transport_domain.enabled);
|
||||
ASSERT_NE (conf.node.ipc_config.transport_domain.io_timeout, defaults.node.ipc_config.transport_domain.io_timeout);
|
||||
ASSERT_NE (conf.node.ipc_config.transport_domain.path, defaults.node.ipc_config.transport_domain.path);
|
||||
ASSERT_NE (conf.node.ipc_config.transport_tcp.enabled, defaults.node.ipc_config.transport_tcp.enabled);
|
||||
ASSERT_NE (conf.node.ipc_config.transport_tcp.io_timeout, defaults.node.ipc_config.transport_tcp.io_timeout);
|
||||
ASSERT_NE (conf.node.ipc_config.transport_tcp.port, defaults.node.ipc_config.transport_tcp.port);
|
||||
|
||||
ASSERT_NE (conf.node.diagnostics_config.txn_tracking.enable, defaults.node.diagnostics_config.txn_tracking.enable);
|
||||
ASSERT_NE (conf.node.diagnostics_config.txn_tracking.ignore_writes_below_block_processor_max_time, defaults.node.diagnostics_config.txn_tracking.ignore_writes_below_block_processor_max_time);
|
||||
ASSERT_NE (conf.node.diagnostics_config.txn_tracking.min_read_txn_time, defaults.node.diagnostics_config.txn_tracking.min_read_txn_time);
|
||||
ASSERT_NE (conf.node.diagnostics_config.txn_tracking.min_write_txn_time, defaults.node.diagnostics_config.txn_tracking.min_write_txn_time);
|
||||
|
||||
ASSERT_NE (conf.node.stat_config.sampling_enabled, defaults.node.stat_config.sampling_enabled);
|
||||
ASSERT_NE (conf.node.stat_config.interval, defaults.node.stat_config.interval);
|
||||
ASSERT_NE (conf.node.stat_config.capacity, defaults.node.stat_config.capacity);
|
||||
ASSERT_NE (conf.node.stat_config.log_rotation_count, defaults.node.stat_config.log_rotation_count);
|
||||
ASSERT_NE (conf.node.stat_config.log_interval_samples, defaults.node.stat_config.log_interval_samples);
|
||||
ASSERT_NE (conf.node.stat_config.log_interval_counters, defaults.node.stat_config.log_interval_counters);
|
||||
ASSERT_NE (conf.node.stat_config.log_headers, defaults.node.stat_config.log_headers);
|
||||
ASSERT_NE (conf.node.stat_config.log_counters_filename, defaults.node.stat_config.log_counters_filename);
|
||||
ASSERT_NE (conf.node.stat_config.log_samples_filename, defaults.node.stat_config.log_samples_filename);
|
||||
}
|
||||
|
||||
/** Deserialize an rpc config with non-default values */
|
||||
TEST (toml, rpc_config_deserialize_no_defaults)
|
||||
{
|
||||
std::stringstream ss;
|
||||
|
||||
// A config file with values that differs from test-net defaults
|
||||
ss << R"toml(
|
||||
address = "0:0:0:0:0:ffff:7f01:101"
|
||||
enable_control = true
|
||||
max_json_depth = 9
|
||||
max_request_size = 999
|
||||
port = 999
|
||||
[process]
|
||||
io_threads = 999
|
||||
ipc_address = "0:0:0:0:0:ffff:7f01:101"
|
||||
ipc_port = 999
|
||||
num_ipc_connections = 999
|
||||
)toml";
|
||||
|
||||
nano::tomlconfig toml;
|
||||
toml.read (ss);
|
||||
nano::rpc_config conf;
|
||||
nano::rpc_config defaults;
|
||||
conf.deserialize_toml (toml);
|
||||
|
||||
ASSERT_FALSE (toml.get_error ()) << toml.get_error ().get_message ();
|
||||
|
||||
ASSERT_NE (conf.address, defaults.address);
|
||||
ASSERT_NE (conf.enable_control, defaults.enable_control);
|
||||
ASSERT_NE (conf.max_json_depth, defaults.max_json_depth);
|
||||
ASSERT_NE (conf.max_request_size, defaults.max_request_size);
|
||||
ASSERT_NE (conf.port, defaults.port);
|
||||
|
||||
ASSERT_NE (conf.rpc_process.io_threads, defaults.rpc_process.io_threads);
|
||||
ASSERT_NE (conf.rpc_process.ipc_address, defaults.rpc_process.ipc_address);
|
||||
ASSERT_NE (conf.rpc_process.ipc_port, defaults.rpc_process.ipc_port);
|
||||
ASSERT_NE (conf.rpc_process.num_ipc_connections, defaults.rpc_process.num_ipc_connections);
|
||||
}
|
|
@ -1,5 +1,4 @@
|
|||
#include <nano/core_test/testutil.hpp>
|
||||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/secure/common.hpp>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
|
|
@ -20,6 +20,7 @@ add_library (nano_lib
|
|||
blocks.cpp
|
||||
config.hpp
|
||||
config.cpp
|
||||
configbase.hpp
|
||||
errors.hpp
|
||||
errors.cpp
|
||||
ipc.hpp
|
||||
|
@ -39,8 +40,11 @@ add_library (nano_lib
|
|||
stats.hpp
|
||||
stats.cpp
|
||||
timer.hpp
|
||||
tomlconfig.hpp
|
||||
utility.hpp
|
||||
utility.cpp
|
||||
walletconfig.hpp
|
||||
walletconfig.cpp
|
||||
work.hpp
|
||||
work.cpp)
|
||||
|
||||
|
|
|
@ -162,6 +162,21 @@ inline boost::filesystem::path get_rpc_config_path (boost::filesystem::path cons
|
|||
return data_path / "rpc_config.json";
|
||||
}
|
||||
|
||||
inline boost::filesystem::path get_node_toml_config_path (boost::filesystem::path const & data_path)
|
||||
{
|
||||
return data_path / "config-node.toml";
|
||||
}
|
||||
|
||||
inline boost::filesystem::path get_rpc_toml_config_path (boost::filesystem::path const & data_path)
|
||||
{
|
||||
return data_path / "config-rpc.toml";
|
||||
}
|
||||
|
||||
inline boost::filesystem::path get_qtwallet_toml_config_path (boost::filesystem::path const & data_path)
|
||||
{
|
||||
return data_path / "config-qtwallet.toml";
|
||||
}
|
||||
|
||||
/** Called by gtest_main to enforce test network */
|
||||
void force_nano_test_network ();
|
||||
|
||||
|
|
97
nano/lib/configbase.hpp
Normal file
97
nano/lib/configbase.hpp
Normal file
|
@ -0,0 +1,97 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/errors.hpp>
|
||||
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/type_traits.hpp>
|
||||
|
||||
#include <istream>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
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
|
||||
|
||||
/** Base type for configuration wrappers */
|
||||
class configbase : public nano::error_aware<>
|
||||
{
|
||||
public:
|
||||
configbase () = default;
|
||||
configbase (std::shared_ptr<nano::error> const & error_a) :
|
||||
error (error_a)
|
||||
{
|
||||
}
|
||||
|
||||
/** Returns the current error */
|
||||
nano::error & get_error () override
|
||||
{
|
||||
return *error;
|
||||
}
|
||||
|
||||
/** Turn on or off automatic error message generation */
|
||||
void set_auto_error_message (bool auto_a)
|
||||
{
|
||||
auto_error_message = auto_a;
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
/** We're a nano::error_aware type. Child nodes share the error state. */
|
||||
std::shared_ptr<nano::error> error;
|
||||
|
||||
/** If set, automatically construct error messages based on parameters and type information. */
|
||||
bool auto_error_message{ true };
|
||||
};
|
||||
}
|
|
@ -1,46 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/boost/asio.hpp>
|
||||
#include <nano/lib/configbase.hpp>
|
||||
#include <nano/lib/errors.hpp>
|
||||
#include <nano/lib/utility.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
|
||||
#include <fstream>
|
||||
|
||||
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<>
|
||||
class jsonconfig : public nano::configbase
|
||||
{
|
||||
public:
|
||||
jsonconfig () :
|
||||
|
@ -50,7 +24,7 @@ public:
|
|||
}
|
||||
|
||||
jsonconfig (boost::property_tree::ptree & tree_a, std::shared_ptr<nano::error> error_a = nullptr) :
|
||||
tree (tree_a), error (error_a)
|
||||
nano::configbase (error_a), tree (tree_a)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
|
@ -234,17 +208,6 @@ public:
|
|||
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)
|
||||
{
|
||||
|
@ -258,6 +221,17 @@ public:
|
|||
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;
|
||||
}
|
||||
|
||||
/** 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)
|
||||
|
@ -300,8 +274,7 @@ public:
|
|||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
* Get value of optional key. Use default value of data type if missing.
|
||||
*/
|
||||
template <typename T>
|
||||
T get (std::string const & key)
|
||||
|
@ -322,44 +295,7 @@ public:
|
|||
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 ())
|
||||
{
|
||||
|
@ -495,10 +431,5 @@ private:
|
|||
/** The property node being managed */
|
||||
boost::property_tree::ptree & tree;
|
||||
boost::property_tree::ptree tree_default;
|
||||
/** 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;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#include <nano/lib/config.hpp>
|
||||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/lib/rpcconfig.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
|
||||
#include <boost/dll/runtime_symbol_info.hpp>
|
||||
|
||||
|
@ -28,6 +29,30 @@ nano::error nano::rpc_secure_config::deserialize_json (nano::jsonconfig & json)
|
|||
return json.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::rpc_secure_config::serialize_toml (nano::tomlconfig & toml) const
|
||||
{
|
||||
toml.put ("enable", enable, "Enable or disable TLS support\ntype:bool");
|
||||
toml.put ("verbose_logging", verbose_logging, "Enable or disable verbose logging\ntype:bool");
|
||||
toml.put ("server_key_passphrase", server_key_passphrase, "Server key passphrase\ntype:string");
|
||||
toml.put ("server_cert_path", server_cert_path, "Directory containing certificates\ntype:string,path");
|
||||
toml.put ("server_key_path", server_key_path, "Path to server key PEM file\ntype:string,path");
|
||||
toml.put ("server_dh_path", server_dh_path, "Path to Diffie-Hellman params file\ntype:string,path");
|
||||
toml.put ("client_certs_path", client_certs_path, "Directory containing client certificates\ntype:string");
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::rpc_secure_config::deserialize_toml (nano::tomlconfig & toml)
|
||||
{
|
||||
toml.get_required<bool> ("enable", enable);
|
||||
toml.get_required<bool> ("verbose_logging", verbose_logging);
|
||||
toml.get_required<std::string> ("server_key_passphrase", server_key_passphrase);
|
||||
toml.get_required<std::string> ("server_cert_path", server_cert_path);
|
||||
toml.get_required<std::string> ("server_key_path", server_key_path);
|
||||
toml.get_required<std::string> ("server_dh_path", server_dh_path);
|
||||
toml.get_required<std::string> ("client_certs_path", client_certs_path);
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::rpc_config::rpc_config (bool enable_control_a) :
|
||||
enable_control (enable_control_a)
|
||||
{
|
||||
|
@ -114,8 +139,114 @@ nano::error nano::rpc_config::deserialize_json (bool & upgraded_a, nano::jsoncon
|
|||
return json.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::rpc_config::serialize_toml (nano::tomlconfig & toml) const
|
||||
{
|
||||
toml.put ("address", address.to_string (), "Bind address for the RPC server\ntype:string,ip");
|
||||
toml.put ("port", port, "Listening port for the RPC server\ntype:uint16");
|
||||
toml.put ("enable_control", enable_control, "Enable or disable control-level requests\ntype:bool");
|
||||
toml.put ("max_json_depth", max_json_depth, "Maximum number of levels in JSON requests\ntype:uint8");
|
||||
toml.put ("max_request_size", max_request_size, "Maximum number of bytes allowed in request bodies\ntype:uint64");
|
||||
|
||||
nano::tomlconfig rpc_process_l;
|
||||
rpc_process_l.put ("io_threads", rpc_process.io_threads, "Number of threads used to serve IO\ntype:uint32");
|
||||
rpc_process_l.put ("ipc_address", rpc_process.ipc_address.to_string (), "Address of IPC server\ntype:string,ip");
|
||||
rpc_process_l.put ("ipc_port", rpc_process.ipc_port, "Listening port of IPC server\ntype:uint16");
|
||||
rpc_process_l.put ("num_ipc_connections", rpc_process.num_ipc_connections, "Number of IPC connections to establish\ntype:uint32");
|
||||
toml.put_child ("process", rpc_process_l);
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::rpc_config::deserialize_toml (nano::tomlconfig & toml)
|
||||
{
|
||||
if (!toml.empty ())
|
||||
{
|
||||
auto rpc_secure_l (toml.get_optional_child ("secure"));
|
||||
if (rpc_secure_l)
|
||||
{
|
||||
secure.deserialize_toml (*rpc_secure_l);
|
||||
}
|
||||
|
||||
toml.get_optional<boost::asio::ip::address_v6> ("address", address);
|
||||
toml.get_optional<uint16_t> ("port", port);
|
||||
toml.get_optional<bool> ("enable_control", enable_control);
|
||||
toml.get_optional<uint8_t> ("max_json_depth", max_json_depth);
|
||||
toml.get_optional<uint64_t> ("max_request_size", max_request_size);
|
||||
|
||||
auto rpc_process_l (toml.get_optional_child ("process"));
|
||||
if (rpc_process_l)
|
||||
{
|
||||
rpc_process_l->get_optional<unsigned> ("io_threads", rpc_process.io_threads);
|
||||
rpc_process_l->get_optional<uint16_t> ("ipc_port", rpc_process.ipc_port);
|
||||
rpc_process_l->get_optional<boost::asio::ip::address_v6> ("ipc_address", rpc_process.ipc_address);
|
||||
rpc_process_l->get_optional<unsigned> ("num_ipc_connections", rpc_process.num_ipc_connections);
|
||||
}
|
||||
}
|
||||
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
nano::error read_rpc_config_toml (boost::filesystem::path const & data_path_a, nano::rpc_config & config_a)
|
||||
{
|
||||
nano::error error;
|
||||
auto json_config_path = nano::get_rpc_config_path (data_path_a);
|
||||
auto toml_config_path = nano::get_rpc_toml_config_path (data_path_a);
|
||||
if (boost::filesystem::exists (json_config_path))
|
||||
{
|
||||
if (boost::filesystem::exists (toml_config_path))
|
||||
{
|
||||
error = "Both json and toml rpc configuration files exists. "
|
||||
"Either remove the config.json file and restart, or remove "
|
||||
"the config-rpc.toml file to start migration on next launch.";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Migrate
|
||||
nano::rpc_config config_json_l;
|
||||
error = read_and_update_rpc_config (data_path_a, config_json_l);
|
||||
|
||||
if (!error)
|
||||
{
|
||||
nano::tomlconfig toml_l;
|
||||
config_json_l.serialize_toml (toml_l);
|
||||
|
||||
// Only write out non-default values
|
||||
nano::rpc_config config_defaults_l;
|
||||
nano::tomlconfig toml_defaults_l;
|
||||
config_defaults_l.serialize_toml (toml_defaults_l);
|
||||
|
||||
toml_l.erase_default_values (toml_defaults_l);
|
||||
if (!toml_l.empty ())
|
||||
{
|
||||
toml_l.write (toml_config_path);
|
||||
boost::system::error_code error_chmod;
|
||||
nano::set_secure_perm_file (toml_config_path, error_chmod);
|
||||
}
|
||||
|
||||
auto backup_path = data_path_a / "rpc_config_backup_toml_migration.json";
|
||||
boost::filesystem::rename (json_config_path, backup_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse and deserialize
|
||||
nano::tomlconfig toml;
|
||||
|
||||
// Make sure we don't create an empty toml file if it doesn't exist. Running without a toml file is the default.
|
||||
if (!error && boost::filesystem::exists (toml_config_path))
|
||||
{
|
||||
error = toml.read (toml_config_path);
|
||||
}
|
||||
|
||||
if (!error)
|
||||
{
|
||||
error = config_a.deserialize_toml (toml);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
nano::error read_and_update_rpc_config (boost::filesystem::path const & data_path, nano::rpc_config & config_a)
|
||||
{
|
||||
boost::system::error_code error_chmod;
|
||||
|
|
|
@ -12,6 +12,7 @@
|
|||
namespace nano
|
||||
{
|
||||
class jsonconfig;
|
||||
class tomlconfig;
|
||||
|
||||
/** Configuration options for RPC TLS */
|
||||
class rpc_secure_config final
|
||||
|
@ -19,6 +20,8 @@ class rpc_secure_config final
|
|||
public:
|
||||
nano::error serialize_json (nano::jsonconfig &) const;
|
||||
nano::error deserialize_json (nano::jsonconfig &);
|
||||
nano::error serialize_toml (nano::tomlconfig &) const;
|
||||
nano::error deserialize_toml (nano::tomlconfig &);
|
||||
|
||||
/** If true, enable TLS */
|
||||
bool enable{ false };
|
||||
|
@ -56,6 +59,8 @@ public:
|
|||
explicit rpc_config (bool = false);
|
||||
nano::error serialize_json (nano::jsonconfig &) const;
|
||||
nano::error deserialize_json (bool & upgraded_a, nano::jsonconfig &);
|
||||
nano::error serialize_toml (nano::tomlconfig &) const;
|
||||
nano::error deserialize_toml (nano::tomlconfig &);
|
||||
|
||||
nano::rpc_process_config rpc_process;
|
||||
boost::asio::ip::address_v6 address{ boost::asio::ip::address_v6::loopback () };
|
||||
|
@ -70,6 +75,7 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
nano::error read_rpc_config_toml (boost::filesystem::path const & data_path_a, nano::rpc_config & config_a);
|
||||
nano::error read_and_update_rpc_config (boost::filesystem::path const & data_path, nano::rpc_config & config_a);
|
||||
|
||||
std::string get_default_rpc_filepath ();
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
#include <nano/boost/asio.hpp>
|
||||
#include <nano/lib/stats.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
|
||||
#include <boost/format.hpp>
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
|
@ -40,6 +41,55 @@ nano::error nano::stat_config::deserialize_json (nano::jsonconfig & json)
|
|||
return json.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::stat_config::deserialize_toml (nano::tomlconfig & toml)
|
||||
{
|
||||
auto sampling_l (toml.get_optional_child ("sampling"));
|
||||
if (sampling_l)
|
||||
{
|
||||
sampling_l->get<bool> ("enable", sampling_enabled);
|
||||
sampling_l->get<size_t> ("capacity", capacity);
|
||||
sampling_l->get<size_t> ("interval", interval);
|
||||
}
|
||||
|
||||
auto log_l (toml.get_optional_child ("log"));
|
||||
if (log_l)
|
||||
{
|
||||
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
|
||||
if (log_counters_filename == log_samples_filename)
|
||||
{
|
||||
toml.get_error ().set ("The statistics counter and samples config values must be different");
|
||||
}
|
||||
}
|
||||
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::stat_config::serialize_toml (nano::tomlconfig & toml) const
|
||||
{
|
||||
nano::tomlconfig sampling_l;
|
||||
sampling_l.put ("enable", sampling_enabled, "Enable or disable samling.\ntype:bool");
|
||||
sampling_l.put ("capacity", capacity, "How many sample intervals to keep in the ring buffer.\ntype:uint64");
|
||||
sampling_l.put ("interval", interval, "Sample interval.\ntype:milliseconds");
|
||||
toml.put_child ("sampling", sampling_l);
|
||||
|
||||
nano::tomlconfig log_l;
|
||||
log_l.put ("headers", log_headers, "If true, write headers on each counter or samples writeout.\nThe header contains log type and the current wall time.\ntype:bool");
|
||||
log_l.put ("interval_counters", log_interval_counters, "How often to log counters. 0 disables logging.\ntype:milliseconds");
|
||||
log_l.put ("interval_samples", log_interval_samples, "How often to log samples. 0 disables logging.\ntype:milliseconds");
|
||||
log_l.put ("rotation_count", log_rotation_count, "Maximum number of log outputs before rotating the file.\ntype:uint64");
|
||||
log_l.put ("filename_counters", log_counters_filename, "Log file name for counters.\ntype:string");
|
||||
log_l.put ("filename_samples", log_samples_filename, "Log file name for samples.\ntype:string");
|
||||
toml.put_child ("log", log_l);
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
std::string nano::stat_log_sink::tm_to_string (tm & tm)
|
||||
{
|
||||
return (boost::format ("%04d.%02d.%02d %02d:%02d:%02d") % (1900 + tm.tm_year) % (tm.tm_mon + 1) % tm.tm_mday % tm.tm_hour % tm.tm_min % tm.tm_sec).str ();
|
||||
|
|
|
@ -18,7 +18,7 @@
|
|||
namespace nano
|
||||
{
|
||||
class node;
|
||||
|
||||
class tomlconfig;
|
||||
/**
|
||||
* Serialize and deserialize the 'statistics' node from config.json
|
||||
* All configuration values have defaults. In particular, file logging of statistics
|
||||
|
@ -29,6 +29,8 @@ class stat_config final
|
|||
public:
|
||||
/** Reads the JSON statistics node */
|
||||
nano::error deserialize_json (nano::jsonconfig & json);
|
||||
nano::error deserialize_toml (nano::tomlconfig & toml);
|
||||
nano::error serialize_toml (nano::tomlconfig & toml) const;
|
||||
|
||||
/** If true, sampling of counters is enabled */
|
||||
bool sampling_enabled{ false };
|
||||
|
|
540
nano/lib/tomlconfig.hpp
Normal file
540
nano/lib/tomlconfig.hpp
Normal file
|
@ -0,0 +1,540 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/boost/asio.hpp>
|
||||
#include <nano/lib/configbase.hpp>
|
||||
#include <nano/lib/errors.hpp>
|
||||
#include <nano/lib/utility.hpp>
|
||||
|
||||
#include <boost/filesystem.hpp>
|
||||
#include <boost/lexical_cast.hpp>
|
||||
#include <boost/optional.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
|
||||
#include <cpptoml.h>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
/** Manages a table in a toml configuration table hierarchy */
|
||||
class tomlconfig : public nano::configbase
|
||||
{
|
||||
public:
|
||||
tomlconfig () :
|
||||
tree (cpptoml::make_table ())
|
||||
{
|
||||
error = std::make_shared<nano::error> ();
|
||||
}
|
||||
|
||||
tomlconfig (std::shared_ptr<cpptoml::table> const & tree_a, std::shared_ptr<nano::error> const & error_a = nullptr) :
|
||||
nano::configbase (error_a), tree (tree_a)
|
||||
{
|
||||
if (!error)
|
||||
{
|
||||
error = std::make_shared<nano::error> ();
|
||||
}
|
||||
}
|
||||
|
||||
void doc (std::string const & key, std::string const & doc)
|
||||
{
|
||||
tree->document (key, doc);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a json object from the stream
|
||||
* @return nano::error&, including a descriptive error message if the config file is malformed.
|
||||
*/
|
||||
nano::error & read (boost::filesystem::path const & path_a)
|
||||
{
|
||||
std::stringstream stream_override_empty;
|
||||
stream_override_empty << std::endl;
|
||||
return read (stream_override_empty, path_a);
|
||||
}
|
||||
|
||||
nano::error & read (std::istream & stream_overrides, boost::filesystem::path const & path_a)
|
||||
{
|
||||
std::fstream stream;
|
||||
open_or_create (stream, path_a.string ());
|
||||
if (!stream.fail ())
|
||||
{
|
||||
try
|
||||
{
|
||||
read (stream_overrides, stream);
|
||||
}
|
||||
catch (std::runtime_error const & ex)
|
||||
{
|
||||
auto pos (stream.tellg ());
|
||||
if (pos != std::streampos (0))
|
||||
{
|
||||
*error = ex;
|
||||
}
|
||||
}
|
||||
stream.close ();
|
||||
}
|
||||
return *error;
|
||||
}
|
||||
|
||||
/** Read from two streams where keys in the first will take precedence over those in the second stream. */
|
||||
void read (std::istream & stream_first_a, std::istream & stream_second_a)
|
||||
{
|
||||
tree = cpptoml::parse_base_and_override_files (stream_first_a, stream_second_a, cpptoml::parser::merge_type::ignore, true);
|
||||
}
|
||||
|
||||
void read (std::istream & stream_a)
|
||||
{
|
||||
std::stringstream stream_override_empty;
|
||||
stream_override_empty << std::endl;
|
||||
tree = cpptoml::parse_base_and_override_files (stream_override_empty, stream_a, cpptoml::parser::merge_type::ignore, true);
|
||||
}
|
||||
|
||||
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
|
||||
{
|
||||
cpptoml::toml_writer writer{ stream_a, "" };
|
||||
tree->accept (writer);
|
||||
}
|
||||
|
||||
/** Open configuration file, create if necessary */
|
||||
void open_or_create (std::fstream & stream_a, std::string const & path_a)
|
||||
{
|
||||
if (!boost::filesystem::exists (path_a))
|
||||
{
|
||||
// Create temp stream to first create the file
|
||||
std::ofstream stream (path_a);
|
||||
|
||||
// Set permissions before opening otherwise Windows only has read permissions
|
||||
nano::set_secure_perm_file (path_a);
|
||||
}
|
||||
|
||||
stream_a.open (path_a);
|
||||
}
|
||||
|
||||
/** Returns the table managed by this instance */
|
||||
std::shared_ptr<cpptoml::table> get_tree ()
|
||||
{
|
||||
return tree;
|
||||
}
|
||||
|
||||
/** Returns true if the toml table is empty */
|
||||
bool empty () const
|
||||
{
|
||||
return tree->empty ();
|
||||
}
|
||||
|
||||
boost::optional<tomlconfig> get_optional_child (std::string const & key_a)
|
||||
{
|
||||
boost::optional<tomlconfig> child_config;
|
||||
if (tree->contains (key_a))
|
||||
{
|
||||
return tomlconfig (tree->get_table (key_a), error);
|
||||
}
|
||||
return child_config;
|
||||
}
|
||||
|
||||
tomlconfig get_required_child (std::string const & key_a)
|
||||
{
|
||||
if (!tree->contains (key_a))
|
||||
{
|
||||
*error = nano::error_config::missing_value;
|
||||
error->set_message ("Missing configuration node: " + key_a);
|
||||
return *this;
|
||||
}
|
||||
else
|
||||
{
|
||||
return tomlconfig (tree->get_table (key_a), error);
|
||||
}
|
||||
}
|
||||
|
||||
tomlconfig & put_child (std::string const & key_a, nano::tomlconfig & conf_a)
|
||||
{
|
||||
tree->insert (key_a, conf_a.get_tree ());
|
||||
return *this;
|
||||
}
|
||||
|
||||
tomlconfig & replace_child (std::string const & key_a, nano::tomlconfig & conf_a)
|
||||
{
|
||||
tree->erase (key_a);
|
||||
put_child (key_a, conf_a);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Set value for the given key. Any existing value will be overwritten. */
|
||||
template <typename T>
|
||||
tomlconfig & put (std::string const & key, T const & value, boost::optional<const char *> documentation_a = boost::none)
|
||||
{
|
||||
tree->insert (key, value);
|
||||
if (documentation_a)
|
||||
{
|
||||
doc (key, *documentation_a);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Returns true if \p key_a is present */
|
||||
bool has_key (std::string const & key_a)
|
||||
{
|
||||
return tree->contains (key_a);
|
||||
}
|
||||
|
||||
/** Erase the property of given key */
|
||||
tomlconfig & erase (std::string const & key_a)
|
||||
{
|
||||
tree->erase (key_a);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Push array element
|
||||
* @param key Array element key. Qualified (dotted) keys are not supported for arrays so this must be called on the correct tomlconfig node.
|
||||
*/
|
||||
template <typename T>
|
||||
tomlconfig & push (std::string const & key, T const & value)
|
||||
{
|
||||
if (!has_key (key))
|
||||
{
|
||||
auto arr = cpptoml::make_array ();
|
||||
tree->insert (key, arr);
|
||||
}
|
||||
auto arr = tree->get_qualified (key)->as_array ();
|
||||
arr->push_back (value);
|
||||
return *this;
|
||||
}
|
||||
|
||||
auto create_array (std::string const & key, boost::optional<const char *> documentation_a)
|
||||
{
|
||||
if (!has_key (key))
|
||||
{
|
||||
auto arr = cpptoml::make_array ();
|
||||
tree->insert (key, arr);
|
||||
if (documentation_a)
|
||||
{
|
||||
doc (key, *documentation_a);
|
||||
}
|
||||
}
|
||||
|
||||
return tree->get_qualified (key)->as_array ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterate array entries.
|
||||
* @param key Array element key. Qualified (dotted) keys are not supported for arrays so this must be called on the correct tomlconfig node.
|
||||
*/
|
||||
template <typename T>
|
||||
tomlconfig & array_entries_required (std::string const & key, std::function<void(T)> callback)
|
||||
{
|
||||
if (tree->contains_qualified (key))
|
||||
{
|
||||
auto items = tree->get_qualified_array_of<T> (key);
|
||||
for (auto & item : *items)
|
||||
{
|
||||
callback (item);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
conditionally_set_error<T> (nano::error_config::missing_value, false, key);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
/** Get optional, using \p default_value if \p key is missing. */
|
||||
template <typename T>
|
||||
tomlconfig & 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>
|
||||
tomlconfig & 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>
|
||||
tomlconfig & get (std::string const & key, T & target)
|
||||
{
|
||||
get_config<T> (true, key, target, target);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get value of optional key. Use default value of data type if missing.
|
||||
*/
|
||||
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>
|
||||
tomlconfig & get_required (std::string const & key, T & target)
|
||||
{
|
||||
get_config<T> (false, key, target);
|
||||
return *this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Erase keys whose values are equal to the one in \p defaults
|
||||
*/
|
||||
void erase_default_values (tomlconfig & defaults_a)
|
||||
{
|
||||
std::shared_ptr<cpptoml::table> clone = std::dynamic_pointer_cast<cpptoml::table> (tree->clone ());
|
||||
tomlconfig self (clone);
|
||||
|
||||
// The toml library doesn't offer a general way to compare values, so let the diff run on a stringified parse
|
||||
std::stringstream ss_self;
|
||||
write (ss_self);
|
||||
self.read (ss_self);
|
||||
|
||||
tomlconfig defaults_l;
|
||||
std::stringstream ss;
|
||||
defaults_a.write (ss);
|
||||
defaults_l.read (ss);
|
||||
|
||||
erase_defaults (defaults_l.get_tree (), self.get_tree (), get_tree ());
|
||||
}
|
||||
|
||||
std::string to_string ()
|
||||
{
|
||||
std::stringstream ss;
|
||||
cpptoml::toml_writer writer{ ss, "" };
|
||||
tree->accept (writer);
|
||||
return ss.str ();
|
||||
}
|
||||
|
||||
protected:
|
||||
template <typename T, typename = std::enable_if_t<nano::is_lexical_castable<T>::value>>
|
||||
tomlconfig & get_config (bool optional, std::string const & key, T & target, T default_value = T ())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (tree->contains_qualified (key))
|
||||
{
|
||||
auto val (tree->get_qualified_as<std::string> (key));
|
||||
if (!boost::conversion::try_lexical_convert<T> (*val, target))
|
||||
{
|
||||
conditionally_set_error<T> (nano::error_config::invalid_value, optional, key);
|
||||
}
|
||||
}
|
||||
else 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>>
|
||||
tomlconfig & get_config (bool optional, std::string const & key, uint8_t & target, uint8_t default_value = T ())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (tree->contains_qualified (key))
|
||||
{
|
||||
int64_t tmp;
|
||||
auto val (tree->get_qualified_as<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);
|
||||
}
|
||||
}
|
||||
else 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>>
|
||||
tomlconfig & get_config (bool optional, std::string const & 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
|
||||
{
|
||||
if (tree->contains_qualified (key))
|
||||
{
|
||||
auto val (tree->get_qualified_as<std::string> (key));
|
||||
bool_conv (*val);
|
||||
}
|
||||
else 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>>
|
||||
tomlconfig & get_config (bool optional, std::string key, boost::asio::ip::address_v6 & target, boost::asio::ip::address_v6 default_value = T ())
|
||||
{
|
||||
try
|
||||
{
|
||||
if (tree->contains_qualified (key))
|
||||
{
|
||||
auto address_l (tree->get_qualified_as<std::string> (key));
|
||||
boost::system::error_code bec;
|
||||
target = boost::asio::ip::address_v6::from_string (address_l.value_or (""), bec);
|
||||
if (bec)
|
||||
{
|
||||
conditionally_set_error<T> (nano::error_config::invalid_value, optional, key);
|
||||
}
|
||||
}
|
||||
else 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;
|
||||
}
|
||||
|
||||
private:
|
||||
/** The config node being managed */
|
||||
std::shared_ptr<cpptoml::table> tree;
|
||||
|
||||
/** Compare two stringified configs, remove keys where values are equal */
|
||||
void erase_defaults (std::shared_ptr<cpptoml::table> base, std::shared_ptr<cpptoml::table> other, std::shared_ptr<cpptoml::table> update_target)
|
||||
{
|
||||
std::vector<std::string> erased;
|
||||
assert (other != nullptr);
|
||||
for (auto & item : *other)
|
||||
{
|
||||
std::string const & key = item.first;
|
||||
if (other->contains (key) && base->contains (key))
|
||||
{
|
||||
auto value = item.second;
|
||||
if (value->is_table ())
|
||||
{
|
||||
auto child_base = base->get_table (key);
|
||||
auto child_other = other->get_table (key);
|
||||
auto child_target = update_target->get_table (key);
|
||||
erase_defaults (child_base, child_other, child_target);
|
||||
if (child_target->empty ())
|
||||
{
|
||||
erased.push_back (key);
|
||||
}
|
||||
}
|
||||
else if (value->is_array ())
|
||||
{
|
||||
auto arr_other = other->get_array (key)->get ();
|
||||
auto arr_base = base->get_array (key)->get ();
|
||||
|
||||
if (arr_other.size () == arr_base.size ())
|
||||
{
|
||||
bool equal = std::equal (arr_other.begin (), arr_other.end (), arr_base.begin (),
|
||||
[](auto const & item1, auto const & item2) -> bool {
|
||||
return (item1->template as<std::string> ()->get () == item2->template as<std::string> ()->get ());
|
||||
});
|
||||
|
||||
if (equal)
|
||||
{
|
||||
erased.push_back (key);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (value->is_value ())
|
||||
{
|
||||
auto val_other = std::dynamic_pointer_cast<cpptoml::value<std::string>> (other->get (key));
|
||||
auto val_base = std::dynamic_pointer_cast<cpptoml::value<std::string>> (base->get (key));
|
||||
|
||||
if (val_other->get () == val_base->get ())
|
||||
{
|
||||
erased.push_back (key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto & key : erased)
|
||||
{
|
||||
update_target->erase (key);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
52
nano/lib/walletconfig.cpp
Normal file
52
nano/lib/walletconfig.cpp
Normal file
|
@ -0,0 +1,52 @@
|
|||
#include <nano/lib/tomlconfig.hpp>
|
||||
#include <nano/lib/walletconfig.hpp>
|
||||
|
||||
nano::wallet_config::wallet_config ()
|
||||
{
|
||||
nano::random_pool::generate_block (wallet.bytes.data (), wallet.bytes.size ());
|
||||
assert (!wallet.is_zero ());
|
||||
}
|
||||
|
||||
nano::error nano::wallet_config::parse (std::string wallet_a, std::string account_a)
|
||||
{
|
||||
nano::error error;
|
||||
if (wallet.decode_hex (wallet_a))
|
||||
{
|
||||
error.set ("Invalid wallet id");
|
||||
}
|
||||
else if (account.decode_account (account_a))
|
||||
{
|
||||
error.set ("Invalid account format");
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
nano::error nano::wallet_config::serialize_toml (nano::tomlconfig & toml) const
|
||||
{
|
||||
std::string wallet_string;
|
||||
wallet.encode_hex (wallet_string);
|
||||
|
||||
toml.put ("wallet", wallet_string, "Wallet identifier\ntype:string,hex");
|
||||
toml.put ("account", account.to_account (), "Current wallet account\ntype:string,account");
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::wallet_config::deserialize_toml (nano::tomlconfig & toml)
|
||||
{
|
||||
std::string wallet_l;
|
||||
std::string account_l;
|
||||
|
||||
toml.get<std::string> ("wallet", wallet_l);
|
||||
toml.get<std::string> ("account", account_l);
|
||||
|
||||
if (wallet.decode_hex (wallet_l))
|
||||
{
|
||||
toml.get_error ().set ("Invalid wallet id. Did you open a node daemon config?");
|
||||
}
|
||||
else if (account.decode_account (account_l))
|
||||
{
|
||||
toml.get_error ().set ("Invalid account");
|
||||
}
|
||||
|
||||
return toml.get_error ();
|
||||
}
|
26
nano/lib/walletconfig.hpp
Normal file
26
nano/lib/walletconfig.hpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/crypto_lib/random_pool.hpp>
|
||||
#include <nano/lib/errors.hpp>
|
||||
#include <nano/lib/numbers.hpp>
|
||||
|
||||
#include <string>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class jsonconfig;
|
||||
class tomlconfig;
|
||||
|
||||
/** Configuration options for the Qt wallet */
|
||||
class wallet_config final
|
||||
{
|
||||
public:
|
||||
wallet_config ();
|
||||
/** Update this instance by parsing the given wallet and account */
|
||||
nano::error parse (std::string wallet_a, std::string account_a);
|
||||
nano::error serialize_toml (nano::tomlconfig & toml_a) const;
|
||||
nano::error deserialize_toml (nano::tomlconfig & toml_a);
|
||||
nano::uint256_union wallet;
|
||||
nano::account account{ 0 };
|
||||
};
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
#include <nano/boost/beast.hpp>
|
||||
#include <nano/boost/process.hpp>
|
||||
#include <nano/core_test/testutil.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
#include <nano/node/daemonconfig.hpp>
|
||||
#include <nano/node/testing.hpp>
|
||||
#include <nano/secure/utility.hpp>
|
||||
|
@ -37,24 +38,27 @@ constexpr auto ipc_port_start = 62000;
|
|||
void write_config_files (boost::filesystem::path const & data_path, int index)
|
||||
{
|
||||
nano::daemon_config daemon_config (data_path);
|
||||
nano::jsonconfig json;
|
||||
json.read_and_update (daemon_config, data_path / "config.json");
|
||||
auto node_l = json.get_required_child ("node");
|
||||
node_l.put ("peering_port", peering_port_start + index);
|
||||
daemon_config.node.peering_port = peering_port_start + index;
|
||||
daemon_config.node.ipc_config.transport_tcp.enabled = true;
|
||||
daemon_config.node.ipc_config.transport_tcp.port = ipc_port_start + index;
|
||||
|
||||
// Alternate use of memory pool
|
||||
node_l.put ("use_memory_pools", (index % 2) == 0);
|
||||
auto tcp = node_l.get_required_child ("ipc").get_required_child ("tcp");
|
||||
tcp.put ("enable", true);
|
||||
tcp.put ("port", ipc_port_start + index);
|
||||
json.write (data_path / "config.json");
|
||||
daemon_config.node.use_memory_pools = (index % 2) == 0;
|
||||
|
||||
// Write daemon config
|
||||
nano::tomlconfig toml;
|
||||
daemon_config.serialize_toml (toml);
|
||||
toml.write (nano::get_node_toml_config_path (data_path));
|
||||
|
||||
nano::rpc_config rpc_config;
|
||||
nano::jsonconfig json1;
|
||||
json1.read_and_update (rpc_config, data_path / "rpc_config.json");
|
||||
json1.put ("port", rpc_port_start + index);
|
||||
json1.put ("enable_control", true);
|
||||
json1.get_required_child ("process").put ("ipc_port", ipc_port_start + index);
|
||||
json1.write (data_path / "rpc_config.json");
|
||||
rpc_config.port = rpc_port_start + index;
|
||||
rpc_config.enable_control = true;
|
||||
rpc_config.rpc_process.ipc_port = ipc_port_start + index;
|
||||
|
||||
// Write rpc config
|
||||
nano::tomlconfig toml_rpc;
|
||||
rpc_config.serialize_toml (toml_rpc);
|
||||
toml_rpc.write (nano::get_rpc_toml_config_path (data_path));
|
||||
}
|
||||
|
||||
// Report a failure
|
||||
|
@ -365,7 +369,7 @@ int main (int argc, char * const * argv)
|
|||
("simultaneous_process_calls", boost::program_options::value<int> ()->default_value (20), "Number of simultaneous rpc sends to do")
|
||||
("destination_count", boost::program_options::value<int> ()->default_value (2), "How many destination accounts to choose between")
|
||||
("node_path", boost::program_options::value<std::string> (), "The path to the nano_node to test")
|
||||
("rpc_path", boost::program_options::value<std::string> (), "The path to do nano_rpc to test");
|
||||
("rpc_path", boost::program_options::value<std::string> (), "The path to the nano_rpc to test");
|
||||
// clang-format on
|
||||
|
||||
boost::program_options::variables_map vm;
|
||||
|
|
|
@ -41,7 +41,7 @@ void nano_daemon::daemon::run (boost::filesystem::path const & data_path, nano::
|
|||
nano::set_secure_perm_directory (data_path, error_chmod);
|
||||
std::unique_ptr<nano::thread_runner> runner;
|
||||
nano::daemon_config config (data_path);
|
||||
auto error = nano::read_and_update_daemon_config (data_path, config);
|
||||
auto error = nano::read_node_config_toml (data_path, config, flags.config_overrides);
|
||||
nano::set_use_memory_pools (config.node.use_memory_pools);
|
||||
if (!error)
|
||||
{
|
||||
|
@ -79,10 +79,11 @@ void nano_daemon::daemon::run (boost::filesystem::path const & data_path, nano::
|
|||
{
|
||||
// Launch rpc in-process
|
||||
nano::rpc_config rpc_config;
|
||||
auto error = nano::read_and_update_rpc_config (data_path, rpc_config);
|
||||
auto error = nano::read_rpc_config_toml (data_path, rpc_config);
|
||||
if (error)
|
||||
{
|
||||
throw std::runtime_error ("Could not deserialize rpc_config file");
|
||||
std::cout << error.get_message () << std::endl;
|
||||
std::exit (1);
|
||||
}
|
||||
rpc_handler = std::make_unique<nano::inprocess_rpc_handler> (*node, config.rpc, [&ipc_server, &alarm, &io_ctx]() {
|
||||
ipc_server.stop ();
|
||||
|
|
|
@ -90,6 +90,7 @@ int main (int argc, char * const * argv)
|
|||
description.add_options ()
|
||||
("help", "Print out options")
|
||||
("version", "Prints out version")
|
||||
("config", boost::program_options::value<std::vector<std::string>>()->multitoken(), "Pass node configuration values. This takes precedence over any values in the configuration file. This option can be repeated multiple times.")
|
||||
("daemon", "Start node daemon")
|
||||
("disable_backup", "Disable wallet automatic backups")
|
||||
("disable_lazy_bootstrap", "Disables lazy bootstrap")
|
||||
|
@ -181,6 +182,12 @@ int main (int argc, char * const * argv)
|
|||
nano_daemon::daemon daemon;
|
||||
nano::node_flags flags;
|
||||
update_flags (flags, vm);
|
||||
|
||||
auto config (vm.find ("config"));
|
||||
if (config != vm.end ())
|
||||
{
|
||||
flags.config_overrides = config->second.as<std::vector<std::string>> ();
|
||||
}
|
||||
daemon.run (data_path, flags);
|
||||
}
|
||||
else if (vm.count ("debug_block_count"))
|
||||
|
|
|
@ -43,7 +43,7 @@ void run (boost::filesystem::path const & data_path)
|
|||
std::unique_ptr<nano::thread_runner> runner;
|
||||
|
||||
nano::rpc_config rpc_config;
|
||||
auto error = nano::read_and_update_rpc_config (data_path, rpc_config);
|
||||
auto error = nano::read_rpc_config_toml (data_path, rpc_config);
|
||||
if (!error)
|
||||
{
|
||||
logging_init (data_path);
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
#include <nano/boost/process.hpp>
|
||||
#include <nano/crypto_lib/random_pool.hpp>
|
||||
#include <nano/lib/errors.hpp>
|
||||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/lib/rpcconfig.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
#include <nano/lib/utility.hpp>
|
||||
#include <nano/lib/walletconfig.hpp>
|
||||
#include <nano/nano_wallet/icon.hpp>
|
||||
#include <nano/node/cli.hpp>
|
||||
#include <nano/node/daemonconfig.hpp>
|
||||
#include <nano/node/ipc.hpp>
|
||||
#include <nano/node/json_handler.hpp>
|
||||
#include <nano/node/node_rpc_config.hpp>
|
||||
|
@ -18,167 +20,6 @@
|
|||
#include <boost/property_tree/json_parser.hpp>
|
||||
#include <boost/property_tree/ptree.hpp>
|
||||
|
||||
class qt_wallet_config final
|
||||
{
|
||||
public:
|
||||
explicit qt_wallet_config (boost::filesystem::path const & data_path_a)
|
||||
{
|
||||
nano::random_pool::generate_block (wallet.bytes.data (), wallet.bytes.size ());
|
||||
assert (!wallet.is_zero ());
|
||||
}
|
||||
bool upgrade_json (unsigned version_a, nano::jsonconfig & json)
|
||||
{
|
||||
json.put ("version", json_version ());
|
||||
switch (version_a)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
nano::account account;
|
||||
account.decode_account (json.get<std::string> ("account"));
|
||||
json.erase ("account");
|
||||
json.put ("account", account.to_account ());
|
||||
json.erase ("version");
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
nano::jsonconfig rpc_l;
|
||||
rpc.serialize_json (rpc_l);
|
||||
json.put ("rpc_enable", "false");
|
||||
json.put_child ("rpc", rpc_l);
|
||||
json.erase ("version");
|
||||
}
|
||||
case 3:
|
||||
{
|
||||
auto opencl_enable_l (json.get_optional<bool> ("opencl_enable"));
|
||||
if (!opencl_enable_l)
|
||||
{
|
||||
json.put ("opencl_enable", "false");
|
||||
}
|
||||
auto opencl_l (json.get_optional_child ("opencl"));
|
||||
if (!opencl_l)
|
||||
{
|
||||
nano::jsonconfig opencl_l;
|
||||
opencl.serialize_json (opencl_l);
|
||||
json.put_child ("opencl", opencl_l);
|
||||
}
|
||||
}
|
||||
case 4:
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error ("Unknown qt_wallet_config version");
|
||||
}
|
||||
return version_a < json_version ();
|
||||
}
|
||||
|
||||
nano::error deserialize_json (bool & upgraded_a, nano::jsonconfig & json)
|
||||
{
|
||||
if (!json.empty ())
|
||||
{
|
||||
auto version_l (json.get_optional<unsigned> ("version"));
|
||||
if (!version_l)
|
||||
{
|
||||
version_l = 1;
|
||||
json.put ("version", version_l.get ());
|
||||
upgraded_a = true;
|
||||
}
|
||||
|
||||
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"));
|
||||
auto rpc_l (json.get_required_child ("rpc"));
|
||||
rpc_enable = json.get<bool> ("rpc_enable");
|
||||
opencl_enable = json.get<bool> ("opencl_enable");
|
||||
auto opencl_l (json.get_required_child ("opencl"));
|
||||
|
||||
if (wallet.decode_hex (wallet_l))
|
||||
{
|
||||
json.get_error ().set ("Invalid wallet id. Did you open a node daemon config?");
|
||||
}
|
||||
else if (account.decode_account (account_l))
|
||||
{
|
||||
json.get_error ().set ("Invalid account");
|
||||
}
|
||||
if (!node_l.get_error ())
|
||||
{
|
||||
node.deserialize_json (upgraded_a, node_l);
|
||||
}
|
||||
if (!rpc_l.get_error ())
|
||||
{
|
||||
rpc.deserialize_json (upgraded_a, rpc_l, data_path);
|
||||
}
|
||||
if (!opencl_l.get_error ())
|
||||
{
|
||||
opencl.deserialize_json (opencl_l);
|
||||
}
|
||||
if (wallet.is_zero ())
|
||||
{
|
||||
nano::random_pool::generate_block (wallet.bytes.data (), wallet.bytes.size ());
|
||||
upgraded_a = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
serialize_json (json);
|
||||
upgraded_a = true;
|
||||
}
|
||||
return json.get_error ();
|
||||
}
|
||||
|
||||
void serialize_json (nano::jsonconfig & json)
|
||||
{
|
||||
std::string wallet_string;
|
||||
wallet.encode_hex (wallet_string);
|
||||
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);
|
||||
json.put_child ("node", node_l);
|
||||
json.put ("rpc_enable", rpc_enable);
|
||||
nano::jsonconfig rpc_l;
|
||||
rpc.serialize_json (rpc_l);
|
||||
json.put_child ("rpc", rpc_l);
|
||||
json.put ("opencl_enable", opencl_enable);
|
||||
nano::jsonconfig opencl_l;
|
||||
opencl.serialize_json (opencl_l);
|
||||
json.put_child ("opencl", opencl_l);
|
||||
}
|
||||
|
||||
bool serialize_json_stream (std::ostream & stream_a)
|
||||
{
|
||||
auto result (false);
|
||||
stream_a.seekp (0);
|
||||
try
|
||||
{
|
||||
nano::jsonconfig json;
|
||||
serialize_json (json);
|
||||
json.write (stream_a);
|
||||
}
|
||||
catch (std::runtime_error const & ex)
|
||||
{
|
||||
std::cerr << ex.what () << std::endl;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
nano::uint256_union wallet;
|
||||
nano::account account{ 0 };
|
||||
nano::node_config node;
|
||||
bool rpc_enable{ false };
|
||||
nano::node_rpc_config rpc;
|
||||
bool opencl_enable{ false };
|
||||
nano::opencl_config opencl;
|
||||
boost::filesystem::path data_path;
|
||||
unsigned json_version () const
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
};
|
||||
|
||||
namespace
|
||||
{
|
||||
void show_error (std::string const & message_a)
|
||||
|
@ -188,33 +29,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)
|
||||
{
|
||||
auto account (config_a.account);
|
||||
auto wallet (config_a.wallet);
|
||||
auto error (false);
|
||||
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;
|
||||
|
||||
// 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);
|
||||
boost::system::error_code error_chmod;
|
||||
nano::set_secure_perm_file (config_path_a, error_chmod);
|
||||
error = config_a.serialize_json_stream (config_file);
|
||||
}
|
||||
}
|
||||
return error;
|
||||
nano::error read_and_update_wallet_config (nano::wallet_config & config_a, boost::filesystem::path const & data_path_a)
|
||||
{
|
||||
nano::tomlconfig wallet_config_toml;
|
||||
auto wallet_path (nano::get_qtwallet_toml_config_path (data_path_a));
|
||||
wallet_config_toml.read (nano::get_qtwallet_toml_config_path (data_path_a));
|
||||
config_a.serialize_toml (wallet_config_toml);
|
||||
|
||||
// Write wallet config. If missing, the file is created and permissions are set.
|
||||
wallet_config_toml.write (wallet_path);
|
||||
return wallet_config_toml.get_error ();
|
||||
}
|
||||
}
|
||||
|
||||
int run_wallet (QApplication & application, int argc, char * const * argv, boost::filesystem::path const & data_path)
|
||||
{
|
||||
int result (0);
|
||||
nano_qt::eventloop_processor processor;
|
||||
boost::system::error_code error_chmod;
|
||||
boost::filesystem::create_directories (data_path);
|
||||
|
@ -225,15 +56,20 @@ int run_wallet (QApplication & application, int argc, char * const * argv, boost
|
|||
application.processEvents ();
|
||||
splash->showMessage (QSplashScreen::tr ("Remember - Back Up Your Wallet Seed"), Qt::AlignBottom | Qt::AlignHCenter, Qt::darkGray);
|
||||
application.processEvents ();
|
||||
qt_wallet_config config (data_path);
|
||||
auto config_path (nano::get_config_path (data_path));
|
||||
int result (0);
|
||||
nano::jsonconfig json;
|
||||
auto error (json.read_and_update (config, config_path));
|
||||
nano::set_use_memory_pools (config.node.use_memory_pools);
|
||||
nano::set_secure_perm_file (config_path, error_chmod);
|
||||
|
||||
nano::daemon_config config (data_path);
|
||||
nano::wallet_config wallet_config;
|
||||
|
||||
auto error = nano::read_node_config_toml (data_path, config);
|
||||
if (!error)
|
||||
{
|
||||
error = read_and_update_wallet_config (wallet_config, data_path);
|
||||
}
|
||||
|
||||
if (!error)
|
||||
{
|
||||
nano::set_use_memory_pools (config.node.use_memory_pools);
|
||||
|
||||
config.node.logging.init (data_path);
|
||||
nano::logger_mt logger{ config.node.logging.min_time_between_log_output };
|
||||
|
||||
|
@ -255,36 +91,36 @@ int run_wallet (QApplication & application, int argc, char * const * argv, boost
|
|||
node = std::make_shared<nano::node> (init, io_ctx, data_path, alarm, config.node, work, flags);
|
||||
if (!init.error ())
|
||||
{
|
||||
auto wallet (node->wallets.open (config.wallet));
|
||||
auto wallet (node->wallets.open (wallet_config.wallet));
|
||||
if (wallet == nullptr)
|
||||
{
|
||||
auto existing (node->wallets.items.begin ());
|
||||
if (existing != node->wallets.items.end ())
|
||||
{
|
||||
wallet = existing->second;
|
||||
config.wallet = existing->first;
|
||||
wallet_config.wallet = existing->first;
|
||||
}
|
||||
else
|
||||
{
|
||||
wallet = node->wallets.create (config.wallet);
|
||||
wallet = node->wallets.create (wallet_config.wallet);
|
||||
}
|
||||
}
|
||||
if (config.account.is_zero () || !wallet->exists (config.account))
|
||||
if (wallet_config.account.is_zero () || !wallet->exists (wallet_config.account))
|
||||
{
|
||||
auto transaction (wallet->wallets.tx_begin_write ());
|
||||
auto existing (wallet->store.begin (transaction));
|
||||
if (existing != wallet->store.end ())
|
||||
{
|
||||
nano::uint256_union account (existing->first);
|
||||
config.account = account;
|
||||
wallet_config.account = account;
|
||||
}
|
||||
else
|
||||
{
|
||||
config.account = wallet->deterministic_insert (transaction);
|
||||
wallet_config.account = wallet->deterministic_insert (transaction);
|
||||
}
|
||||
}
|
||||
assert (wallet->exists (config.account));
|
||||
update_config (config, config_path);
|
||||
assert (wallet->exists (wallet_config.account));
|
||||
read_and_update_wallet_config (wallet_config, data_path);
|
||||
node->start ();
|
||||
nano::ipc::ipc_server ipc (*node, config.rpc);
|
||||
|
||||
|
@ -299,10 +135,11 @@ int run_wallet (QApplication & application, int argc, char * const * argv, boost
|
|||
{
|
||||
// Launch rpc in-process
|
||||
nano::rpc_config rpc_config;
|
||||
auto error = nano::read_and_update_rpc_config (data_path, rpc_config);
|
||||
auto error = nano::read_rpc_config_toml (data_path, rpc_config);
|
||||
if (error)
|
||||
{
|
||||
throw std::runtime_error ("Could not deserialize rpc_config file");
|
||||
std::cout << error.get_message () << std::endl;
|
||||
std::exit (1);
|
||||
}
|
||||
rpc_handler = std::make_unique<nano::inprocess_rpc_handler> (*node, config.rpc);
|
||||
rpc = nano::get_rpc (io_ctx, rpc_config, *rpc_handler);
|
||||
|
@ -340,7 +177,7 @@ int run_wallet (QApplication & application, int argc, char * const * argv, boost
|
|||
runner.stop_event_processing ();
|
||||
});
|
||||
application.postEvent (&processor, new nano_qt::eventloop_event ([&]() {
|
||||
gui = std::make_shared<nano_qt::wallet> (application, processor, *node, wallet, config.account);
|
||||
gui = std::make_shared<nano_qt::wallet> (application, processor, *node, wallet, wallet_config.account);
|
||||
splash->close ();
|
||||
gui->start ();
|
||||
gui->client_window->show ();
|
||||
|
@ -353,12 +190,12 @@ int run_wallet (QApplication & application, int argc, char * const * argv, boost
|
|||
splash->hide ();
|
||||
show_error ("Error initializing node");
|
||||
}
|
||||
update_config (config, config_path);
|
||||
read_and_update_wallet_config (wallet_config, data_path);
|
||||
}
|
||||
else
|
||||
{
|
||||
splash->hide ();
|
||||
show_error ("Error deserializing config: " + json.get_error ().get_message ());
|
||||
show_error ("Error deserializing config: " + error.get_message ());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <nano/lib/config.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
#include <nano/node/cli.hpp>
|
||||
#include <nano/node/common.hpp>
|
||||
#include <nano/node/daemonconfig.hpp>
|
||||
|
@ -43,6 +44,7 @@ void nano::add_node_options (boost::program_options::options_description & descr
|
|||
("unchecked_clear", "Clear unchecked blocks")
|
||||
("confirmation_height_clear", "Clear confirmation height")
|
||||
("diagnostics", "Run internal diagnostics")
|
||||
("generate_config", boost::program_options::value<std::string> (), "Write configuration to stdout, populated with defaults suitable for this system. Pass the configuration type node or rpc.")
|
||||
("key_create", "Generates a adhoc random keypair and prints it to stdout")
|
||||
("key_expand", "Derive public key and account number from <key>")
|
||||
("wallet_add_adhoc", "Insert <key> in to <wallet>")
|
||||
|
@ -346,20 +348,32 @@ std::error_code nano::handle_node_options (boost::program_options::variables_map
|
|||
std::cout << "Confirmation heights of all accounts (except genesis) are set to 0" << std::endl;
|
||||
}
|
||||
}
|
||||
else if (vm.count ("generate_config"))
|
||||
{
|
||||
auto type = vm["generate_config"].as<std::string> ();
|
||||
|
||||
if (type == "node")
|
||||
{
|
||||
nano::daemon_config config (data_path);
|
||||
nano::tomlconfig toml;
|
||||
config.serialize_toml (toml);
|
||||
std::cout << toml.to_string () << std::endl;
|
||||
}
|
||||
else if (type == "rpc")
|
||||
{
|
||||
nano::rpc_config config (false);
|
||||
nano::tomlconfig toml;
|
||||
config.serialize_toml (toml);
|
||||
std::cout << toml.to_string () << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Invalid configuration type " << type << ". Must be node or rpc." << std::endl;
|
||||
}
|
||||
}
|
||||
else if (vm.count ("diagnostics"))
|
||||
{
|
||||
inactive_node node (data_path);
|
||||
|
||||
// Check/upgrade the config.json file.
|
||||
{
|
||||
nano::daemon_config config (data_path);
|
||||
auto error = nano::read_and_update_daemon_config (data_path, config);
|
||||
if (error)
|
||||
{
|
||||
std::cerr << "Error deserializing config: " << error.get_message () << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Testing hash function" << std::endl;
|
||||
nano::raw_key key;
|
||||
key.data.clear ();
|
||||
|
|
|
@ -1,11 +1,68 @@
|
|||
#include <nano/lib/config.hpp>
|
||||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
#include <nano/lib/walletconfig.hpp>
|
||||
#include <nano/node/daemonconfig.hpp>
|
||||
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
nano::daemon_config::daemon_config (boost::filesystem::path const & data_path_a) :
|
||||
data_path (data_path_a)
|
||||
{
|
||||
}
|
||||
|
||||
nano::error nano::daemon_config::serialize_toml (nano::tomlconfig & toml)
|
||||
{
|
||||
nano::tomlconfig rpc_l;
|
||||
rpc.serialize_toml (rpc_l);
|
||||
rpc_l.doc ("enable", "Enable or disable RPC\ntype:bool");
|
||||
rpc_l.put ("enable", rpc_enable);
|
||||
toml.put_child ("rpc", rpc_l);
|
||||
|
||||
nano::tomlconfig node_l;
|
||||
node.serialize_toml (node_l);
|
||||
nano::tomlconfig node (node_l);
|
||||
toml.put_child ("node", node);
|
||||
|
||||
nano::tomlconfig opencl_l;
|
||||
opencl.serialize_toml (opencl_l);
|
||||
opencl_l.doc ("enable", "Enable or disable OpenCL work generation\ntype:bool");
|
||||
opencl_l.put ("enable", opencl_enable);
|
||||
toml.put_child ("opencl", opencl_l);
|
||||
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::daemon_config::deserialize_toml (nano::tomlconfig & toml)
|
||||
{
|
||||
auto rpc_l (toml.get_optional_child ("rpc"));
|
||||
|
||||
if (!toml.get_error () && rpc_l)
|
||||
{
|
||||
rpc_l->get_optional<bool> ("enable", rpc_enable);
|
||||
rpc.deserialize_toml (*rpc_l);
|
||||
}
|
||||
|
||||
auto node_l (toml.get_optional_child ("node"));
|
||||
if (!toml.get_error () && node_l)
|
||||
{
|
||||
node.deserialize_toml (*node_l);
|
||||
}
|
||||
|
||||
if (!toml.get_error ())
|
||||
{
|
||||
auto opencl_l (toml.get_optional_child ("opencl"));
|
||||
if (!toml.get_error () && opencl_l)
|
||||
{
|
||||
opencl_l->get_optional<bool> ("enable", opencl_enable);
|
||||
opencl.deserialize_toml (*opencl_l);
|
||||
}
|
||||
}
|
||||
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::daemon_config::serialize_json (nano::jsonconfig & json)
|
||||
{
|
||||
json.put ("version", json_version ());
|
||||
|
@ -35,9 +92,6 @@ nano::error nano::daemon_config::deserialize_json (bool & upgraded_a, nano::json
|
|||
{
|
||||
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);
|
||||
|
||||
auto rpc_l (json.get_required_child ("rpc"));
|
||||
|
@ -74,43 +128,110 @@ nano::error nano::daemon_config::deserialize_json (bool & upgraded_a, nano::json
|
|||
return json.get_error ();
|
||||
}
|
||||
|
||||
bool nano::daemon_config::upgrade_json (unsigned version_a, nano::jsonconfig & json)
|
||||
{
|
||||
json.put ("version", json_version ());
|
||||
switch (version_a)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
bool opencl_enable_l{ false };
|
||||
json.get_optional<bool> ("opencl_enable", opencl_enable_l);
|
||||
if (!opencl_enable_l)
|
||||
{
|
||||
json.put ("opencl_enable", false);
|
||||
}
|
||||
auto opencl_l (json.get_optional_child ("opencl"));
|
||||
if (!opencl_l)
|
||||
{
|
||||
nano::jsonconfig opencl_l;
|
||||
opencl.serialize_json (opencl_l);
|
||||
json.put_child ("opencl", opencl_l);
|
||||
}
|
||||
}
|
||||
case 2:
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error ("Unknown daemon_config version");
|
||||
}
|
||||
return version_a < json_version ();
|
||||
}
|
||||
|
||||
namespace nano
|
||||
{
|
||||
nano::error read_and_update_daemon_config (boost::filesystem::path const & data_path, nano::daemon_config & config_a)
|
||||
nano::error read_node_config_toml (boost::filesystem::path const & data_path_a, nano::daemon_config & config_a, std::vector<std::string> const & config_overrides)
|
||||
{
|
||||
nano::error error;
|
||||
auto json_config_path = nano::get_config_path (data_path_a);
|
||||
auto toml_config_path = nano::get_node_toml_config_path (data_path_a);
|
||||
auto toml_qt_config_path = nano::get_qtwallet_toml_config_path (data_path_a);
|
||||
if (boost::filesystem::exists (json_config_path))
|
||||
{
|
||||
if (boost::filesystem::exists (toml_config_path))
|
||||
{
|
||||
error = "Both json and toml node configuration files exists. "
|
||||
"Either remove the config.json file and restart, or remove "
|
||||
"the config-node.toml file to start migration on next launch.";
|
||||
}
|
||||
else
|
||||
{
|
||||
// Migrate
|
||||
nano::daemon_config config_old_l;
|
||||
nano::jsonconfig json;
|
||||
read_and_update_daemon_config (data_path_a, config_old_l, json);
|
||||
error = json.get_error ();
|
||||
|
||||
// Move qt wallet entries to wallet config file
|
||||
if (!error && json.has_key ("wallet") && json.has_key ("account"))
|
||||
{
|
||||
if (!boost::filesystem::exists (toml_config_path))
|
||||
{
|
||||
nano::wallet_config wallet_conf;
|
||||
error = wallet_conf.parse (json.get<std::string> ("wallet"), json.get<std::string> ("account"));
|
||||
if (!error)
|
||||
{
|
||||
nano::tomlconfig wallet_toml_l;
|
||||
wallet_conf.serialize_toml (wallet_toml_l);
|
||||
wallet_toml_l.write (toml_qt_config_path);
|
||||
|
||||
boost::system::error_code error_chmod;
|
||||
nano::set_secure_perm_file (toml_qt_config_path, error_chmod);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cout << "Not migrating wallet and account as wallet config file already exists" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (!error)
|
||||
{
|
||||
nano::tomlconfig toml_l;
|
||||
config_old_l.serialize_toml (toml_l);
|
||||
|
||||
// Only write out non-default values
|
||||
nano::daemon_config config_defaults_l;
|
||||
nano::tomlconfig toml_defaults_l;
|
||||
config_defaults_l.serialize_toml (toml_defaults_l);
|
||||
|
||||
toml_l.erase_default_values (toml_defaults_l);
|
||||
if (!toml_l.empty ())
|
||||
{
|
||||
toml_l.write (toml_config_path);
|
||||
boost::system::error_code error_chmod;
|
||||
nano::set_secure_perm_file (toml_config_path, error_chmod);
|
||||
}
|
||||
|
||||
auto backup_path = data_path_a / "config_backup_toml_migration.json";
|
||||
boost::filesystem::rename (json_config_path, backup_path);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Parse and deserialize
|
||||
nano::tomlconfig toml;
|
||||
|
||||
std::stringstream config_stream;
|
||||
for (auto const & entry : config_overrides)
|
||||
{
|
||||
config_stream << entry << std::endl;
|
||||
}
|
||||
config_stream << std::endl;
|
||||
|
||||
// Make sure we don't create an empty toml file if it doesn't exist. Running without a toml file is the default.
|
||||
if (!error && boost::filesystem::exists (toml_config_path))
|
||||
{
|
||||
toml.read (config_stream, toml_config_path);
|
||||
}
|
||||
else if (!error)
|
||||
{
|
||||
toml.read (config_stream);
|
||||
}
|
||||
|
||||
if (!error)
|
||||
{
|
||||
error = config_a.deserialize_toml (toml);
|
||||
}
|
||||
|
||||
return error;
|
||||
}
|
||||
|
||||
nano::error read_and_update_daemon_config (boost::filesystem::path const & data_path, nano::daemon_config & config_a, nano::jsonconfig & json_a)
|
||||
{
|
||||
boost::system::error_code error_chmod;
|
||||
nano::jsonconfig json;
|
||||
auto config_path = nano::get_config_path (data_path);
|
||||
auto error (json.read_and_update (config_a, config_path));
|
||||
auto error (json_a.read_and_update (config_a, config_path));
|
||||
nano::set_secure_perm_file (config_path, error_chmod);
|
||||
return error;
|
||||
}
|
||||
|
|
|
@ -5,20 +5,21 @@
|
|||
#include <nano/node/nodeconfig.hpp>
|
||||
#include <nano/node/openclconfig.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class jsonconfig;
|
||||
class tomlconfig;
|
||||
class daemon_config
|
||||
{
|
||||
public:
|
||||
daemon_config () = default;
|
||||
daemon_config (boost::filesystem::path const & data_path);
|
||||
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);
|
||||
nano::error deserialize_toml (nano::tomlconfig &);
|
||||
nano::error serialize_toml (nano::tomlconfig &);
|
||||
bool rpc_enable{ false };
|
||||
nano::node_rpc_config rpc;
|
||||
nano::node_config node;
|
||||
|
@ -31,5 +32,6 @@ public:
|
|||
}
|
||||
};
|
||||
|
||||
nano::error read_and_update_daemon_config (boost::filesystem::path const &, nano::daemon_config & config_a);
|
||||
nano::error read_node_config_toml (boost::filesystem::path const &, nano::daemon_config & config_a, std::vector<std::string> const & config_overrides = std::vector<std::string> ());
|
||||
nano::error read_and_update_daemon_config (boost::filesystem::path const &, nano::daemon_config & config_a, nano::jsonconfig & json_a);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
#include <nano/node/diagnosticsconfig.hpp>
|
||||
|
||||
nano::error nano::diagnostics_config::serialize_json (nano::jsonconfig & json) const
|
||||
|
@ -30,3 +31,33 @@ nano::error nano::diagnostics_config::deserialize_json (nano::jsonconfig & json)
|
|||
}
|
||||
return json.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::diagnostics_config::serialize_toml (nano::tomlconfig & toml) const
|
||||
{
|
||||
nano::tomlconfig txn_tracking_l;
|
||||
txn_tracking_l.put ("enable", txn_tracking.enable, "Enable or disable database transaction tracing\ntype:bool");
|
||||
txn_tracking_l.put ("min_read_txn_time", txn_tracking.min_read_txn_time.count (), "Log stacktrace when read transactions are held longer than this duration\ntype:milliseconds");
|
||||
txn_tracking_l.put ("min_write_txn_time", txn_tracking.min_write_txn_time.count (), "Log stacktrace when write transactions are held longer than this duration\ntype:milliseconds");
|
||||
txn_tracking_l.put ("ignore_writes_below_block_processor_max_time", txn_tracking.ignore_writes_below_block_processor_max_time, "Ignore any block processor writes less than block_processor_batch_max_time\ntype:bool");
|
||||
toml.put_child ("txn_tracking", txn_tracking_l);
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::diagnostics_config::deserialize_toml (nano::tomlconfig & toml)
|
||||
{
|
||||
auto txn_tracking_l (toml.get_optional_child ("txn_tracking"));
|
||||
if (txn_tracking_l)
|
||||
{
|
||||
txn_tracking_l->get_optional<bool> ("enable", txn_tracking.enable);
|
||||
auto min_read_txn_time_l = static_cast<unsigned long> (txn_tracking.min_read_txn_time.count ());
|
||||
txn_tracking_l->get_optional ("min_read_txn_time", min_read_txn_time_l);
|
||||
txn_tracking.min_read_txn_time = std::chrono::milliseconds (min_read_txn_time_l);
|
||||
|
||||
auto min_write_txn_time_l = static_cast<unsigned long> (txn_tracking.min_write_txn_time.count ());
|
||||
txn_tracking_l->get_optional ("min_write_txn_time", min_write_txn_time_l);
|
||||
txn_tracking.min_write_txn_time = std::chrono::milliseconds (min_write_txn_time_l);
|
||||
|
||||
txn_tracking_l->get_optional<bool> ("ignore_writes_below_block_processor_max_time", txn_tracking.ignore_writes_below_block_processor_max_time);
|
||||
}
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
namespace nano
|
||||
{
|
||||
class jsonconfig;
|
||||
|
||||
class tomlconfig;
|
||||
class txn_tracking_config final
|
||||
{
|
||||
public:
|
||||
|
@ -24,6 +24,8 @@ class diagnostics_config final
|
|||
public:
|
||||
nano::error serialize_json (nano::jsonconfig &) const;
|
||||
nano::error deserialize_json (nano::jsonconfig &);
|
||||
nano::error serialize_toml (nano::tomlconfig &) const;
|
||||
nano::error deserialize_toml (nano::tomlconfig &);
|
||||
|
||||
txn_tracking_config txn_tracking;
|
||||
};
|
||||
|
|
|
@ -1,6 +1,58 @@
|
|||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
#include <nano/node/ipcconfig.hpp>
|
||||
|
||||
nano::error nano::ipc::ipc_config::serialize_toml (nano::tomlconfig & toml) const
|
||||
{
|
||||
nano::tomlconfig tcp_l;
|
||||
// Only write out experimental config values if they're previously set explicitly in the config file
|
||||
if (transport_tcp.io_threads >= 0)
|
||||
{
|
||||
tcp_l.put ("io_threads", transport_tcp.io_threads);
|
||||
}
|
||||
tcp_l.put ("enable", transport_tcp.enabled);
|
||||
tcp_l.put ("port", transport_tcp.port);
|
||||
tcp_l.put ("io_timeout", transport_tcp.io_timeout);
|
||||
toml.put_child ("tcp", tcp_l);
|
||||
|
||||
nano::tomlconfig domain_l;
|
||||
if (transport_domain.io_threads >= 0)
|
||||
{
|
||||
domain_l.put ("io_threads", transport_domain.io_threads);
|
||||
}
|
||||
domain_l.put ("enable", transport_domain.enabled);
|
||||
domain_l.put ("allow_unsafe", transport_domain.allow_unsafe);
|
||||
domain_l.put ("path", transport_domain.path);
|
||||
domain_l.put ("io_timeout", transport_domain.io_timeout);
|
||||
toml.put_child ("local", domain_l);
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::ipc::ipc_config::deserialize_toml (nano::tomlconfig & toml)
|
||||
{
|
||||
auto tcp_l (toml.get_optional_child ("tcp"));
|
||||
if (tcp_l)
|
||||
{
|
||||
tcp_l->get_optional<long> ("io_threads", transport_tcp.io_threads, -1);
|
||||
tcp_l->get<bool> ("allow_unsafe", transport_tcp.allow_unsafe);
|
||||
tcp_l->get<bool> ("enable", transport_tcp.enabled);
|
||||
tcp_l->get<uint16_t> ("port", transport_tcp.port);
|
||||
tcp_l->get<size_t> ("io_timeout", transport_tcp.io_timeout);
|
||||
}
|
||||
|
||||
auto domain_l (toml.get_optional_child ("local"));
|
||||
if (domain_l)
|
||||
{
|
||||
domain_l->get_optional<long> ("io_threads", transport_domain.io_threads, -1);
|
||||
domain_l->get<bool> ("allow_unsafe", transport_domain.allow_unsafe);
|
||||
domain_l->get<bool> ("enable", transport_domain.enabled);
|
||||
domain_l->get<std::string> ("path", transport_domain.path);
|
||||
domain_l->get<size_t> ("io_timeout", transport_domain.io_timeout);
|
||||
}
|
||||
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::ipc::ipc_config::serialize_json (nano::jsonconfig & json) const
|
||||
{
|
||||
nano::jsonconfig tcp_l;
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
namespace nano
|
||||
{
|
||||
class jsonconfig;
|
||||
|
||||
class tomlconfig;
|
||||
namespace ipc
|
||||
{
|
||||
/** Base class for transport configurations */
|
||||
|
@ -57,6 +57,8 @@ namespace ipc
|
|||
public:
|
||||
nano::error deserialize_json (bool & upgraded_a, nano::jsonconfig & json_a);
|
||||
nano::error serialize_json (nano::jsonconfig & json) const;
|
||||
nano::error deserialize_toml (nano::tomlconfig & toml_a);
|
||||
nano::error serialize_toml (nano::tomlconfig & toml) const;
|
||||
ipc_config_domain_socket transport_domain;
|
||||
ipc_config_tcp_socket transport_tcp;
|
||||
};
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
#include <nano/lib/config.hpp>
|
||||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
#include <nano/node/logging.hpp>
|
||||
|
||||
#include <boost/date_time/posix_time/posix_time.hpp>
|
||||
|
@ -80,6 +82,66 @@ void nano::logging::release_file_sink ()
|
|||
}
|
||||
}
|
||||
|
||||
nano::error nano::logging::serialize_toml (nano::tomlconfig & toml) const
|
||||
{
|
||||
toml.put ("ledger", ledger_logging_value, "Log ledger related messages\ntype:bool");
|
||||
toml.put ("ledger_duplicate", ledger_duplicate_logging_value, "Log when a duplicate block is attempted inserted into the ledger\ntype:bool");
|
||||
toml.put ("vote", vote_logging_value, "Vote logging. Enabling this option leads to a high volume\nof log messages which may affect node performance\ntype:bool");
|
||||
toml.put ("network", network_logging_value, "Log network related messages\ntype:bool");
|
||||
toml.put ("network_timeout", network_timeout_logging_value, "Log TCP timeouts\ntype:bool");
|
||||
toml.put ("network_message", network_message_logging_value, "Log network errors and message details\ntype:bool");
|
||||
toml.put ("network_publish", network_publish_logging_value, "Log publish related network messages\ntype:bool");
|
||||
toml.put ("network_packet", network_packet_logging_value, "Log network packet activity\ntype:bool");
|
||||
toml.put ("network_keepalive", network_keepalive_logging_value, "Log keepalive related messages\ntype:bool");
|
||||
toml.put ("network_node_id_handshake", network_node_id_handshake_logging_value, "Log node-id handshake related messages\ntype:bool");
|
||||
toml.put ("node_lifetime_tracing", node_lifetime_tracing_value, "Log node startup and shutdown messages\ntype:bool");
|
||||
toml.put ("insufficient_work", insufficient_work_logging_value, "Log if insufficient work is detected\ntype:bool");
|
||||
toml.put ("log_ipc", log_ipc_value, "Log IPC related activity\ntype:bool");
|
||||
toml.put ("bulk_pull", bulk_pull_logging_value, "Log bulk pull errors and messages\ntype:bool");
|
||||
toml.put ("work_generation_time", work_generation_time_value, "Log work generation timing information\ntype:bool");
|
||||
toml.put ("upnp_details", upnp_details_logging_value, "Log UPNP discovery details. WARNING: this may include information\nabout discovered devices, such as product identification. Please review before sharing logs.\ntype:bool");
|
||||
toml.put ("timing", timing_logging_value, "Log detailed timing information for various node operations\ntype:bool");
|
||||
toml.put ("log_to_cerr", log_to_cerr_value, "Log to standard error in addition to the log file\ntype:bool");
|
||||
toml.put ("max_size", max_size, "Maximum log file size in bytes\ntype:uint64");
|
||||
toml.put ("rotation_size", rotation_size, "Log file rotation size in character count\ntype:uint64");
|
||||
toml.put ("flush", flush, "If enabled, immediately flush new entries to log file. This may negatively affect logging performance.\ntype:bool");
|
||||
toml.put ("min_time_between_output", min_time_between_log_output.count (), "Minimum time that must pass for low priority entries to be logged\ntype:milliseconds");
|
||||
toml.put ("single_line_record", single_line_record_value, "Keep log entries on single lines\ntype:bool");
|
||||
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::logging::deserialize_toml (nano::tomlconfig & toml)
|
||||
{
|
||||
toml.get<bool> ("ledger", ledger_logging_value);
|
||||
toml.get<bool> ("ledger_duplicate", ledger_duplicate_logging_value);
|
||||
toml.get<bool> ("vote", vote_logging_value);
|
||||
toml.get<bool> ("network", network_logging_value);
|
||||
toml.get<bool> ("network_timeout", network_timeout_logging_value);
|
||||
toml.get<bool> ("network_message", network_message_logging_value);
|
||||
toml.get<bool> ("network_publish", network_publish_logging_value);
|
||||
toml.get<bool> ("network_packet", network_packet_logging_value);
|
||||
toml.get<bool> ("network_keepalive", network_keepalive_logging_value);
|
||||
toml.get<bool> ("network_node_id_handshake", network_node_id_handshake_logging_value);
|
||||
toml.get<bool> ("node_lifetime_tracing", node_lifetime_tracing_value);
|
||||
toml.get<bool> ("insufficient_work", insufficient_work_logging_value);
|
||||
toml.get<bool> ("log_ipc", log_ipc_value);
|
||||
toml.get<bool> ("bulk_pull", bulk_pull_logging_value);
|
||||
toml.get<bool> ("work_generation_time", work_generation_time_value);
|
||||
toml.get<bool> ("upnp_details", upnp_details_logging_value);
|
||||
toml.get<bool> ("timing", timing_logging_value);
|
||||
toml.get<bool> ("log_to_cerr", log_to_cerr_value);
|
||||
toml.get<bool> ("flush", flush);
|
||||
toml.get<bool> ("single_line_record", single_line_record_value);
|
||||
toml.get<uintmax_t> ("max_size", max_size);
|
||||
toml.get<uintmax_t> ("rotation_size", rotation_size);
|
||||
uintmax_t min_time_between_log_output_raw;
|
||||
toml.get<uintmax_t> ("min_time_between_output", min_time_between_log_output_raw);
|
||||
min_time_between_log_output = std::chrono::milliseconds (min_time_between_log_output_raw);
|
||||
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::logging::serialize_json (nano::jsonconfig & json) const
|
||||
{
|
||||
json.put ("version", json_version ());
|
||||
|
|
|
@ -15,11 +15,14 @@
|
|||
|
||||
namespace nano
|
||||
{
|
||||
class tomlconfig;
|
||||
class logging final
|
||||
{
|
||||
public:
|
||||
nano::error serialize_json (nano::jsonconfig &) const;
|
||||
nano::error deserialize_json (bool &, nano::jsonconfig &);
|
||||
nano::error serialize_toml (nano::tomlconfig &) const;
|
||||
nano::error deserialize_toml (nano::tomlconfig &);
|
||||
bool upgrade_json (unsigned, nano::jsonconfig &);
|
||||
bool ledger_logging () const;
|
||||
bool ledger_duplicate_logging () const;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <nano/lib/config.hpp>
|
||||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/lib/rpcconfig.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
#include <nano/node/node_rpc_config.hpp>
|
||||
|
||||
nano::error nano::node_rpc_config::serialize_json (nano::jsonconfig & json) const
|
||||
|
@ -17,6 +18,39 @@ nano::error nano::node_rpc_config::serialize_json (nano::jsonconfig & json) cons
|
|||
return json.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::node_rpc_config::serialize_toml (nano::tomlconfig & toml) const
|
||||
{
|
||||
toml.put ("enable_sign_hash", enable_sign_hash, "Allow or disallow signing of hashes\ntype:bool");
|
||||
toml.put ("max_work_generate_difficulty", nano::to_string_hex (max_work_generate_difficulty), "Maximum allowed difficulty request for work generation\ntype:string,hex");
|
||||
|
||||
nano::tomlconfig child_process_l;
|
||||
child_process_l.put ("enable", child_process.enable, "Enable or disable RPC child process. If false, an in-process RPC server is used.\ntype:bool");
|
||||
child_process_l.put ("rpc_path", child_process.rpc_path, "Path to the nano_rpc executable. Must be set if child process is enabled.\ntype:string,path");
|
||||
toml.put_child ("child_process", child_process_l);
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::node_rpc_config::deserialize_toml (nano::tomlconfig & toml)
|
||||
{
|
||||
toml.get_optional ("enable_sign_hash", enable_sign_hash);
|
||||
toml.get_optional<bool> ("enable_sign_hash", enable_sign_hash);
|
||||
std::string max_work_generate_difficulty_text;
|
||||
toml.get_optional<std::string> ("max_work_generate_difficulty", max_work_generate_difficulty_text);
|
||||
if (!max_work_generate_difficulty_text.empty ())
|
||||
{
|
||||
nano::from_string_hex (max_work_generate_difficulty_text, max_work_generate_difficulty);
|
||||
}
|
||||
|
||||
auto child_process_l (toml.get_optional_child ("child_process"));
|
||||
if (child_process_l)
|
||||
{
|
||||
child_process_l->get_optional<bool> ("enable", child_process.enable);
|
||||
child_process_l->get_optional<std::string> ("rpc_path", child_process.rpc_path);
|
||||
}
|
||||
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::node_rpc_config::deserialize_json (bool & upgraded_a, nano::jsonconfig & json, boost::filesystem::path const & data_path)
|
||||
{
|
||||
auto version_l (json.get_optional<unsigned> ("version"));
|
||||
|
|
|
@ -8,6 +8,7 @@
|
|||
|
||||
namespace nano
|
||||
{
|
||||
class tomlconfig;
|
||||
class rpc_child_process_config final
|
||||
{
|
||||
public:
|
||||
|
@ -20,6 +21,9 @@ class node_rpc_config final
|
|||
public:
|
||||
nano::error serialize_json (nano::jsonconfig &) const;
|
||||
nano::error deserialize_json (bool & upgraded_a, nano::jsonconfig &, boost::filesystem::path const & data_path);
|
||||
nano::error serialize_toml (nano::tomlconfig & toml) const;
|
||||
nano::error deserialize_toml (nano::tomlconfig & toml);
|
||||
|
||||
bool enable_sign_hash{ false };
|
||||
uint64_t max_work_generate_difficulty{ 0xffffffffc0000000 };
|
||||
nano::rpc_child_process_config child_process;
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
#include <nano/lib/config.hpp>
|
||||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/lib/rpcconfig.hpp>
|
||||
#include <nano/lib/tomlconfig.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
|
||||
|
@ -62,6 +63,281 @@ logging (logging_a)
|
|||
}
|
||||
}
|
||||
|
||||
nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const
|
||||
{
|
||||
toml.put ("peering_port", peering_port, "Node peering port\ntype:uint16");
|
||||
toml.put ("bootstrap_fraction_numerator", bootstrap_fraction_numerator, "Change bootstrap threshold (online stake / 256 * bootstrap_fraction_numerator)\ntype:uint32");
|
||||
toml.put ("receive_minimum", receive_minimum.to_string_dec (), "Minimum receive amount\ntype:string,amount,raw");
|
||||
toml.put ("online_weight_minimum", online_weight_minimum.to_string_dec (), "Online weight minimum required to confirm block\ntype:string,amount,raw");
|
||||
toml.put ("online_weight_quorum", online_weight_quorum, "Percentage of votes required to rollback blocks\ntype:uint64");
|
||||
toml.put ("password_fanout", password_fanout, "Password fanout factor\ntype:uint64");
|
||||
toml.put ("io_threads", io_threads, "Number of threads dedicated to I/O opeations\ntype:uint64");
|
||||
toml.put ("network_threads", network_threads, "Number of threads dedicated to processing network messages\ntype:uint64");
|
||||
toml.put ("work_threads", work_threads, "Number of threads dedicated to CPU generated work. Defaults to all available CPU threads.\ntype:uint64");
|
||||
toml.put ("signature_checker_threads", signature_checker_threads, "Number of additional threads dedicated to signature verification\ntype:uint64");
|
||||
toml.put ("enable_voting", enable_voting, "Enable or disable voting. Enabling voting requires additional system resources.\ntype:bool");
|
||||
toml.put ("bootstrap_connections", bootstrap_connections, "Number of outbound bootstrap connections. Must be a power of 2.\ntype:uint64");
|
||||
toml.put ("bootstrap_connections_max", bootstrap_connections_max, "Maximum number of inbound bootstrap connections\ntype:uint64");
|
||||
toml.put ("lmdb_max_dbs", lmdb_max_dbs, "Maximum open lmdb databases. Increase default if more than 100 wallets is required.\ntype:uint64");
|
||||
toml.put ("block_processor_batch_max_time", block_processor_batch_max_time.count (), "The maximum time the block processor can process blocks at a time\ntype:milliseconds");
|
||||
toml.put ("allow_local_peers", allow_local_peers, "Enable or disable local host peering\ntype:bool");
|
||||
toml.put ("vote_minimum", vote_minimum.to_string_dec (), "Do not vote if delegated weight is under this threshold\ntype:string,amount,raw");
|
||||
toml.put ("vote_generator_delay", vote_generator_delay.count (), "Delay before votes are sent to allow for better bundling of hashes in votes.\nHigh performance nodes may need slightly higher values to optimize vote bandwidth.\ntype:milliseconds");
|
||||
toml.put ("vote_generator_threshold", vote_generator_threshold, "Number of bundled hashes required for an additional generator delay\ntype:uint64,[1..11]");
|
||||
toml.put ("unchecked_cutoff_time", unchecked_cutoff_time.count (), "Number of seconds unchecked entry survives before being cleaned\ntype:seconds");
|
||||
toml.put ("tcp_io_timeout", tcp_io_timeout.count (), "Timeout for TCP connect-, read- and write operations\ntype:seconds");
|
||||
toml.put ("pow_sleep_interval", pow_sleep_interval.count (), "The amount to sleep after each batch of POW calculations. Reduces max CPU usage at the expensive of a longer.\ntype:nanoseconds");
|
||||
toml.put ("external_address", external_address.to_string (), "The external address of this node (NAT). If not set, the node will request this information via UPnP.\ntype:string,ip");
|
||||
toml.put ("external_port", external_port, "The external port number of this node (NAT). If not set, the node will request this information via UPnP.\ntype:uint16");
|
||||
toml.put ("tcp_incoming_connections_max", tcp_incoming_connections_max, "Maximum number of incoming TCP connections\ntype:uint64");
|
||||
toml.put ("use_memory_pools", use_memory_pools, "If true, allocate memory from memory pools. Enabling this may improve performance. Memory is never released to the OS.\ntype:bool");
|
||||
toml.put ("confirmation_history_size", confirmation_history_size, "Maximum confirmation history size\ntype:uint64");
|
||||
toml.put ("active_elections_size", active_elections_size, "Limits number of active elections before dropping will be considered (other conditions must also be satisfied)\ntype:uint64,[250..]");
|
||||
toml.put ("bandwidth_limit", bandwidth_limit, "Outbound traffic limit in bytes/sec after which messages will be dropped\ntype:uint64");
|
||||
toml.put ("backup_before_upgrade", backup_before_upgrade, "Backup the ledger database before performing upgrades\ntype:bool");
|
||||
toml.put ("work_watcher_period", work_watcher_period.count (), "Time between checks for confirmation and re-generating higher difficulty work if unconfirmed, for blocks in the work watcher.\ntype:seconds");
|
||||
|
||||
auto work_peers_l (toml.create_array ("work_peers", "A list of \"address:port\" entries to identify work peers"));
|
||||
for (auto i (work_peers.begin ()), n (work_peers.end ()); i != n; ++i)
|
||||
{
|
||||
work_peers_l->push_back (boost::str (boost::format ("%1%:%2%") % i->first % i->second));
|
||||
}
|
||||
|
||||
auto preconfigured_peers_l (toml.create_array ("preconfigured_peers", "A list of \"address:port\" entries to identify preconfigured peers"));
|
||||
for (auto i (preconfigured_peers.begin ()), n (preconfigured_peers.end ()); i != n; ++i)
|
||||
{
|
||||
preconfigured_peers_l->push_back (*i);
|
||||
}
|
||||
|
||||
auto preconfigured_representatives_l (toml.create_array ("preconfigured_representatives", "A list of representative account addresses"));
|
||||
for (auto i (preconfigured_representatives.begin ()), n (preconfigured_representatives.end ()); i != n; ++i)
|
||||
{
|
||||
preconfigured_representatives_l->push_back (i->to_account ());
|
||||
}
|
||||
nano::tomlconfig callback_l;
|
||||
callback_l.put ("address", callback_address, "Callback address\ntype:string,ip");
|
||||
callback_l.put ("port", callback_port, "Callback port number\ntype:uint16");
|
||||
callback_l.put ("target", callback_target, "Callback target path\ntype:string,uri");
|
||||
toml.put_child ("httpcallback", callback_l);
|
||||
|
||||
nano::tomlconfig logging_l;
|
||||
logging.serialize_toml (logging_l);
|
||||
toml.put_child ("logging", logging_l);
|
||||
|
||||
nano::tomlconfig websocket_l;
|
||||
websocket_config.serialize_toml (websocket_l);
|
||||
toml.put_child ("websocket", websocket_l);
|
||||
|
||||
nano::tomlconfig ipc_l;
|
||||
ipc_config.serialize_toml (ipc_l);
|
||||
toml.put_child ("ipc", ipc_l);
|
||||
|
||||
nano::tomlconfig diagnostics_l;
|
||||
diagnostics_config.serialize_toml (diagnostics_l);
|
||||
toml.put_child ("diagnostics", diagnostics_l);
|
||||
|
||||
nano::tomlconfig stat_l;
|
||||
stat_config.serialize_toml (stat_l);
|
||||
toml.put_child ("statistics", stat_l);
|
||||
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::node_config::deserialize_toml (nano::tomlconfig & toml)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (toml.has_key ("httpcallback"))
|
||||
{
|
||||
auto callback_l (toml.get_required_child ("httpcallback"));
|
||||
callback_l.get<std::string> ("address", callback_address);
|
||||
callback_l.get<uint16_t> ("port", callback_port);
|
||||
callback_l.get<std::string> ("target", callback_target);
|
||||
}
|
||||
|
||||
if (toml.has_key ("logging"))
|
||||
{
|
||||
auto logging_l (toml.get_required_child ("logging"));
|
||||
logging.deserialize_toml (logging_l);
|
||||
}
|
||||
|
||||
if (toml.has_key ("websocket"))
|
||||
{
|
||||
auto websocket_config_l (toml.get_required_child ("websocket"));
|
||||
websocket_config.deserialize_toml (websocket_config_l);
|
||||
}
|
||||
|
||||
if (toml.has_key ("ipc"))
|
||||
{
|
||||
auto ipc_config_l (toml.get_required_child ("ipc"));
|
||||
ipc_config.deserialize_toml (ipc_config_l);
|
||||
}
|
||||
|
||||
if (toml.has_key ("diagnostics"))
|
||||
{
|
||||
auto diagnostics_config_l (toml.get_required_child ("diagnostics"));
|
||||
diagnostics_config.deserialize_toml (diagnostics_config_l);
|
||||
}
|
||||
|
||||
if (toml.has_key ("statistics"))
|
||||
{
|
||||
auto stat_config_l (toml.get_required_child ("statistics"));
|
||||
stat_config.deserialize_toml (stat_config_l);
|
||||
}
|
||||
|
||||
if (toml.has_key ("work_peers"))
|
||||
{
|
||||
work_peers.clear ();
|
||||
toml.array_entries_required<std::string> ("work_peers", [this](std::string const & entry) {
|
||||
auto port_position (entry.rfind (':'));
|
||||
bool result = port_position == -1;
|
||||
if (!result)
|
||||
{
|
||||
auto port_str (entry.substr (port_position + 1));
|
||||
uint16_t port;
|
||||
result |= parse_port (port_str, port);
|
||||
if (!result)
|
||||
{
|
||||
auto address (entry.substr (0, port_position));
|
||||
this->work_peers.emplace_back (address, port);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (toml.has_key (preconfigured_peers_key))
|
||||
{
|
||||
preconfigured_peers.clear ();
|
||||
toml.array_entries_required<std::string> (preconfigured_peers_key, [this](std::string entry) {
|
||||
preconfigured_peers.push_back (entry);
|
||||
});
|
||||
}
|
||||
|
||||
if (toml.has_key ("preconfigured_representatives"))
|
||||
{
|
||||
preconfigured_representatives.clear ();
|
||||
toml.array_entries_required<std::string> ("preconfigured_representatives", [this, &toml](std::string entry) {
|
||||
nano::account representative (0);
|
||||
if (representative.decode_account (entry))
|
||||
{
|
||||
toml.get_error ().set ("Invalid representative account: " + entry);
|
||||
}
|
||||
preconfigured_representatives.push_back (representative);
|
||||
});
|
||||
}
|
||||
|
||||
if (preconfigured_representatives.empty ())
|
||||
{
|
||||
toml.get_error ().set ("At least one representative account must be set");
|
||||
}
|
||||
|
||||
auto receive_minimum_l (toml.get<std::string> ("receive_minimum"));
|
||||
if (receive_minimum.decode_dec (receive_minimum_l))
|
||||
{
|
||||
toml.get_error ().set ("receive_minimum contains an invalid decimal amount");
|
||||
}
|
||||
|
||||
auto online_weight_minimum_l (toml.get<std::string> ("online_weight_minimum"));
|
||||
if (online_weight_minimum.decode_dec (online_weight_minimum_l))
|
||||
{
|
||||
toml.get_error ().set ("online_weight_minimum contains an invalid decimal amount");
|
||||
}
|
||||
|
||||
auto vote_minimum_l (toml.get<std::string> ("vote_minimum"));
|
||||
if (vote_minimum.decode_dec (vote_minimum_l))
|
||||
{
|
||||
toml.get_error ().set ("vote_minimum contains an invalid decimal amount");
|
||||
}
|
||||
|
||||
auto delay_l = vote_generator_delay.count ();
|
||||
toml.get ("vote_generator_delay", delay_l);
|
||||
vote_generator_delay = std::chrono::milliseconds (delay_l);
|
||||
|
||||
toml.get<unsigned> ("vote_generator_threshold", vote_generator_threshold);
|
||||
|
||||
auto block_processor_batch_max_time_l (toml.get<unsigned long> ("block_processor_batch_max_time"));
|
||||
block_processor_batch_max_time = std::chrono::milliseconds (block_processor_batch_max_time_l);
|
||||
auto unchecked_cutoff_time_l = static_cast<unsigned long> (unchecked_cutoff_time.count ());
|
||||
toml.get ("unchecked_cutoff_time", unchecked_cutoff_time_l);
|
||||
unchecked_cutoff_time = std::chrono::seconds (unchecked_cutoff_time_l);
|
||||
|
||||
auto tcp_io_timeout_l = static_cast<unsigned long> (tcp_io_timeout.count ());
|
||||
toml.get ("tcp_io_timeout", tcp_io_timeout_l);
|
||||
tcp_io_timeout = std::chrono::seconds (tcp_io_timeout_l);
|
||||
|
||||
toml.get<uint16_t> ("peering_port", peering_port);
|
||||
toml.get<unsigned> ("bootstrap_fraction_numerator", bootstrap_fraction_numerator);
|
||||
toml.get<unsigned> ("online_weight_quorum", online_weight_quorum);
|
||||
toml.get<unsigned> ("password_fanout", password_fanout);
|
||||
toml.get<unsigned> ("io_threads", io_threads);
|
||||
toml.get<unsigned> ("work_threads", work_threads);
|
||||
toml.get<unsigned> ("network_threads", network_threads);
|
||||
toml.get<unsigned> ("bootstrap_connections", bootstrap_connections);
|
||||
toml.get<unsigned> ("bootstrap_connections_max", bootstrap_connections_max);
|
||||
toml.get<int> ("lmdb_max_dbs", lmdb_max_dbs);
|
||||
toml.get<bool> ("enable_voting", enable_voting);
|
||||
toml.get<bool> ("allow_local_peers", allow_local_peers);
|
||||
toml.get<unsigned> (signature_checker_threads_key, signature_checker_threads);
|
||||
toml.get<boost::asio::ip::address_v6> ("external_address", external_address);
|
||||
toml.get<uint16_t> ("external_port", external_port);
|
||||
toml.get<unsigned> ("tcp_incoming_connections_max", tcp_incoming_connections_max);
|
||||
|
||||
auto pow_sleep_interval_l (pow_sleep_interval.count ());
|
||||
toml.get (pow_sleep_interval_key, pow_sleep_interval_l);
|
||||
pow_sleep_interval = std::chrono::nanoseconds (pow_sleep_interval_l);
|
||||
toml.get<bool> ("use_memory_pools", use_memory_pools);
|
||||
toml.get<size_t> ("confirmation_history_size", confirmation_history_size);
|
||||
toml.get<size_t> ("active_elections_size", active_elections_size);
|
||||
toml.get<size_t> ("bandwidth_limit", bandwidth_limit);
|
||||
toml.get<bool> ("backup_before_upgrade", backup_before_upgrade);
|
||||
|
||||
auto work_watcher_period_l = work_watcher_period.count ();
|
||||
toml.get ("work_watcher_period", work_watcher_period_l);
|
||||
work_watcher_period = std::chrono::seconds (work_watcher_period_l);
|
||||
|
||||
auto conf_height_processor_batch_min_time_l (conf_height_processor_batch_min_time.count ());
|
||||
toml.get ("conf_height_processor_batch_min_time", conf_height_processor_batch_min_time_l);
|
||||
conf_height_processor_batch_min_time = std::chrono::milliseconds (conf_height_processor_batch_min_time_l);
|
||||
|
||||
nano::network_constants network;
|
||||
// Validate ranges
|
||||
if (online_weight_quorum > 100)
|
||||
{
|
||||
toml.get_error ().set ("online_weight_quorum must be less than 100");
|
||||
}
|
||||
if (password_fanout < 16 || password_fanout > 1024 * 1024)
|
||||
{
|
||||
toml.get_error ().set ("password_fanout must be a number between 16 and 1048576");
|
||||
}
|
||||
if (io_threads == 0)
|
||||
{
|
||||
toml.get_error ().set ("io_threads must be non-zero");
|
||||
}
|
||||
if (active_elections_size <= 250 && !network.is_test_network ())
|
||||
{
|
||||
toml.get_error ().set ("active_elections_size must be greater than 250");
|
||||
}
|
||||
if (bandwidth_limit > std::numeric_limits<size_t>::max ())
|
||||
{
|
||||
toml.get_error ().set ("bandwidth_limit unbounded = 0, default = 5242880, max = 18446744073709551615");
|
||||
}
|
||||
if (vote_generator_threshold < 1 || vote_generator_threshold > 11)
|
||||
{
|
||||
toml.get_error ().set ("vote_generator_threshold must be a number between 1 and 11");
|
||||
}
|
||||
if (work_watcher_period < std::chrono::seconds (1))
|
||||
{
|
||||
toml.get_error ().set ("work_watcher_period must be equal or larger than 1");
|
||||
}
|
||||
}
|
||||
catch (std::runtime_error const & ex)
|
||||
{
|
||||
toml.get_error ().set (ex.what ());
|
||||
}
|
||||
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::node_config::serialize_json (nano::jsonconfig & json) const
|
||||
{
|
||||
json.put ("version", json_version ());
|
||||
|
|
|
@ -16,6 +16,7 @@
|
|||
|
||||
namespace nano
|
||||
{
|
||||
class tomlconfig;
|
||||
/**
|
||||
* Node configuration
|
||||
*/
|
||||
|
@ -26,6 +27,8 @@ public:
|
|||
node_config (uint16_t, nano::logging const &);
|
||||
nano::error serialize_json (nano::jsonconfig &) const;
|
||||
nano::error deserialize_json (bool &, nano::jsonconfig &);
|
||||
nano::error serialize_toml (nano::tomlconfig &) const;
|
||||
nano::error deserialize_toml (nano::tomlconfig &);
|
||||
bool upgrade_json (unsigned, nano::jsonconfig &);
|
||||
nano::account random_representative ();
|
||||
nano::network_params network_params;
|
||||
|
@ -88,6 +91,7 @@ public:
|
|||
class node_flags final
|
||||
{
|
||||
public:
|
||||
std::vector<std::string> config_overrides;
|
||||
bool disable_backup{ false };
|
||||
bool disable_lazy_bootstrap{ false };
|
||||
bool disable_legacy_bootstrap{ false };
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
#include <nano/node/openclconfig.hpp>
|
||||
|
||||
nano::opencl_config::opencl_config (unsigned platform_a, unsigned device_a, unsigned threads_a) :
|
||||
|
@ -22,3 +24,25 @@ nano::error nano::opencl_config::deserialize_json (nano::jsonconfig & json)
|
|||
json.get_optional<unsigned> ("threads", threads);
|
||||
return json.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::opencl_config::serialize_toml (nano::tomlconfig & toml) const
|
||||
{
|
||||
toml.put ("platform", platform);
|
||||
toml.put ("device", device);
|
||||
toml.put ("threads", threads);
|
||||
|
||||
// Add documentation
|
||||
toml.doc ("platform", "OpenCL platform identifier");
|
||||
toml.doc ("device", "OpenCL device identifier");
|
||||
toml.doc ("threads", "OpenCL thread count");
|
||||
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::opencl_config::deserialize_toml (nano::tomlconfig & toml)
|
||||
{
|
||||
toml.get_optional<unsigned> ("platform", platform);
|
||||
toml.get_optional<unsigned> ("device", device);
|
||||
toml.get_optional<unsigned> ("threads", threads);
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
|
|
@ -1,10 +1,11 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/errors.hpp>
|
||||
#include <nano/lib/jsonconfig.hpp>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
class jsonconfig;
|
||||
class tomlconfig;
|
||||
class opencl_config
|
||||
{
|
||||
public:
|
||||
|
@ -12,6 +13,8 @@ public:
|
|||
opencl_config (unsigned, unsigned, unsigned);
|
||||
nano::error serialize_json (nano::jsonconfig &) const;
|
||||
nano::error deserialize_json (nano::jsonconfig &);
|
||||
nano::error serialize_toml (nano::tomlconfig &) const;
|
||||
nano::error deserialize_toml (nano::tomlconfig &);
|
||||
unsigned platform{ 0 };
|
||||
unsigned device{ 0 };
|
||||
unsigned threads{ 1024 * 1024 };
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
#include <nano/lib/jsonconfig.hpp>
|
||||
#include <nano/lib/tomlconfig.hpp>
|
||||
#include <nano/node/websocketconfig.hpp>
|
||||
|
||||
nano::websocket::config::config () :
|
||||
|
@ -6,6 +7,22 @@ port (network_constants.default_websocket_port)
|
|||
{
|
||||
}
|
||||
|
||||
nano::error nano::websocket::config::serialize_toml (nano::tomlconfig & toml) const
|
||||
{
|
||||
toml.put ("enable", enabled, "Enable or disable WebSocket server\ntype:bool");
|
||||
toml.put ("address", address.to_string (), "WebSocket server bind address\ntype:string,ip");
|
||||
toml.put ("port", port, "WebSocket server listening port\ntype:uint16");
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::websocket::config::deserialize_toml (nano::tomlconfig & toml)
|
||||
{
|
||||
toml.get<bool> ("enable", enabled);
|
||||
toml.get_required<boost::asio::ip::address_v6> ("address", address);
|
||||
toml.get<uint16_t> ("port", port);
|
||||
return toml.get_error ();
|
||||
}
|
||||
|
||||
nano::error nano::websocket::config::serialize_json (nano::jsonconfig & json) const
|
||||
{
|
||||
json.put ("enable", enabled);
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
namespace nano
|
||||
{
|
||||
class jsonconfig;
|
||||
class tomlconfig;
|
||||
namespace websocket
|
||||
{
|
||||
/** websocket configuration */
|
||||
|
@ -16,6 +17,8 @@ namespace websocket
|
|||
config ();
|
||||
nano::error deserialize_json (nano::jsonconfig & json_a);
|
||||
nano::error serialize_json (nano::jsonconfig & json) const;
|
||||
nano::error deserialize_toml (nano::tomlconfig & toml_a);
|
||||
nano::error serialize_toml (nano::tomlconfig & toml) const;
|
||||
nano::network_constants network_constants;
|
||||
bool enabled{ false };
|
||||
uint16_t port;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue