diff --git a/nano/core_test/toml.cpp b/nano/core_test/toml.cpp index 0de22167be..69ca02fc28 100644 --- a/nano/core_test/toml.cpp +++ b/nano/core_test/toml.cpp @@ -93,7 +93,6 @@ TEST (toml, daemon_config_deserialize_defaults) 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); } @@ -257,6 +256,7 @@ TEST (toml, daemon_config_deserialize_no_defaults) work_peers = ["test.org:999"] work_threads = 999 work_watcher_period = 999 + max_work_generate_multiplier = 1.0 [node.diagnostics.txn_tracking] enable = true ignore_writes_below_block_processor_max_time = false @@ -331,7 +331,6 @@ TEST (toml, daemon_config_deserialize_no_defaults) [rpc] enable = true enable_sign_hash = true - max_work_generate_difficulty = "ffffffffc9999999" [rpc.child_process] enable = true @@ -352,7 +351,6 @@ TEST (toml, daemon_config_deserialize_no_defaults) 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); @@ -370,6 +368,7 @@ TEST (toml, daemon_config_deserialize_no_defaults) 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.max_work_generate_multiplier, defaults.node.max_work_generate_multiplier); 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); @@ -536,3 +535,20 @@ TEST (toml, rpc_config_no_required) ASSERT_FALSE (toml.get_error ()) << toml.get_error ().get_message (); } + +/** Deserialize a node config with incorrect values */ +TEST (toml, daemon_config_deserialize_errors) +{ + std::stringstream ss_max_work_generate_multiplier; + ss_max_work_generate_multiplier << R"toml( + [node] + max_work_generate_multiplier = 0.9 + )toml"; + + nano::tomlconfig toml; + toml.read (ss_max_work_generate_multiplier); + nano::daemon_config conf; + conf.deserialize_toml (toml); + + ASSERT_EQ (toml.get_error ().get_message (), "max_work_generate_multiplier must be greater than or equal to 1"); +} diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 1c5976f6f2..c2081b3be6 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -727,6 +727,11 @@ uint64_t nano::active_transactions::active_difficulty () return trended_active_difficulty; } +uint64_t nano::active_transactions::limited_active_difficulty () +{ + return std::min (active_difficulty (), node.config.max_work_generate_difficulty); +} + // List of active blocks in elections std::deque> nano::active_transactions::list_blocks (bool single_lock) { diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index 64c1094870..4158945dc3 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -96,6 +96,7 @@ class active_transactions final void adjust_difficulty (nano::block_hash const &); void update_active_difficulty (std::unique_lock &); uint64_t active_difficulty (); + uint64_t limited_active_difficulty (); std::deque> list_blocks (bool = false); void erase (nano::block const &); //drop 2 from roots based on adjusted_difficulty diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index a834a00901..ca58159930 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -4440,7 +4440,7 @@ void nano::json_handler::work_generate () auto difficulty (difficulty_optional_impl ()); auto multiplier (multiplier_optional_impl (difficulty)); (void)multiplier; - if (!ec && (difficulty > node_rpc_config.max_work_generate_difficulty || difficulty < node.network_params.network.publish_threshold)) + if (!ec && (difficulty > node.config.max_work_generate_difficulty || difficulty < node.network_params.network.publish_threshold)) { ec = nano::error_rpc::difficulty_limit; } diff --git a/nano/node/node_rpc_config.cpp b/nano/node/node_rpc_config.cpp index af9814e40b..b9b1e8ef06 100644 --- a/nano/node/node_rpc_config.cpp +++ b/nano/node/node_rpc_config.cpp @@ -9,7 +9,6 @@ nano::error nano::node_rpc_config::serialize_json (nano::jsonconfig & json) cons { json.put ("version", json_version ()); json.put ("enable_sign_hash", enable_sign_hash); - json.put ("max_work_generate_difficulty", nano::to_string_hex (max_work_generate_difficulty)); nano::jsonconfig child_process_l; child_process_l.put ("enable", child_process.enable); @@ -21,7 +20,6 @@ nano::error nano::node_rpc_config::serialize_json (nano::jsonconfig & json) cons 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"); @@ -34,12 +32,6 @@ nano::error nano::node_rpc_config::deserialize_toml (nano::tomlconfig & toml) { toml.get_optional ("enable_sign_hash", enable_sign_hash); toml.get_optional ("enable_sign_hash", enable_sign_hash); - std::string max_work_generate_difficulty_text; - toml.get_optional ("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) @@ -68,7 +60,6 @@ nano::error nano::node_rpc_config::deserialize_json (bool & upgraded_a, nano::js migrate (json, data_path); json.put ("enable_sign_hash", enable_sign_hash); - json.put ("max_work_generate_difficulty", nano::to_string_hex (max_work_generate_difficulty)); // Remove options no longer needed after migration json.erase ("enable_control"); @@ -88,12 +79,6 @@ nano::error nano::node_rpc_config::deserialize_json (bool & upgraded_a, nano::js } json.get_optional ("enable_sign_hash", enable_sign_hash); - std::string max_work_generate_difficulty_text; - json.get_optional ("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 (json.get_optional_child ("child_process")); if (child_process_l) diff --git a/nano/node/node_rpc_config.hpp b/nano/node/node_rpc_config.hpp index c88297aa8d..f175e1f0f6 100644 --- a/nano/node/node_rpc_config.hpp +++ b/nano/node/node_rpc_config.hpp @@ -25,7 +25,6 @@ class node_rpc_config final 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; static unsigned json_version () { diff --git a/nano/node/nodeconfig.cpp b/nano/node/nodeconfig.cpp index 34db24f76a..f21702e731 100644 --- a/nano/node/nodeconfig.cpp +++ b/nano/node/nodeconfig.cpp @@ -35,6 +35,7 @@ logging (logging_a) const char * epoch_message ("epoch v1 block"); strncpy ((char *)epoch_block_link.bytes.data (), epoch_message, epoch_block_link.bytes.size ()); epoch_block_signer = network_params.ledger.genesis_account; + max_work_generate_difficulty = nano::difficulty::from_multiplier (max_work_generate_multiplier, network_params.network.publish_threshold); switch (network_params.network.network ()) { case nano::nano_networks::nano_test_network: @@ -96,6 +97,7 @@ nano::error nano::node_config::serialize_toml (nano::tomlconfig & toml) const 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"); + toml.put ("max_work_generate_multiplier", max_work_generate_multiplier, "Maximum allowed difficulty multiplier for work generation\ntype:double,[1..]"); 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) @@ -300,6 +302,9 @@ nano::error nano::node_config::deserialize_toml (nano::tomlconfig & toml) conf_height_processor_batch_min_time = std::chrono::milliseconds (conf_height_processor_batch_min_time_l); nano::network_constants network; + toml.get ("max_work_generate_multiplier", max_work_generate_multiplier); + max_work_generate_difficulty = nano::difficulty::from_multiplier (max_work_generate_multiplier, network.publish_threshold); + // Validate ranges if (online_weight_quorum > 100) { @@ -329,6 +334,10 @@ nano::error nano::node_config::deserialize_toml (nano::tomlconfig & toml) { toml.get_error ().set ("work_watcher_period must be equal or larger than 1"); } + if (max_work_generate_multiplier < 1) + { + toml.get_error ().set ("max_work_generate_multiplier must be greater than or equal to 1"); + } } catch (std::runtime_error const & ex) { @@ -716,7 +725,7 @@ nano::error nano::node_config::deserialize_json (bool & upgraded_a, nano::jsonco } if (active_elections_size <= 250 && !network.is_test_network ()) { - json.get_error ().set ("active_elections_size must be grater than 250"); + json.get_error ().set ("active_elections_size must be greater than 250"); } if (bandwidth_limit > std::numeric_limits::max ()) { diff --git a/nano/node/nodeconfig.hpp b/nano/node/nodeconfig.hpp index eb4922c457..56d3f668ce 100644 --- a/nano/node/nodeconfig.hpp +++ b/nano/node/nodeconfig.hpp @@ -82,6 +82,8 @@ class node_config std::chrono::milliseconds conf_height_processor_batch_min_time{ 50 }; bool backup_before_upgrade{ false }; std::chrono::seconds work_watcher_period{ std::chrono::seconds (5) }; + double max_work_generate_multiplier{ 64. }; + uint64_t max_work_generate_difficulty{ nano::network_constants::publish_full_threshold }; static unsigned json_version () { return 18; diff --git a/nano/node/wallet.cpp b/nano/node/wallet.cpp index 2018957764..d8df208561 100644 --- a/nano/node/wallet.cpp +++ b/nano/node/wallet.cpp @@ -978,7 +978,7 @@ std::shared_ptr nano::wallet::receive_action (nano::block const & s if (nano::work_validate (*block)) { wallets.node.logger.try_log (boost::str (boost::format ("Cached or provided work for block %1% account %2% is invalid, regenerating") % block->hash ().to_string () % account.to_account ())); - wallets.node.work_generate_blocking (*block, wallets.node.active.active_difficulty ()); + wallets.node.work_generate_blocking (*block, wallets.node.active.limited_active_difficulty ()); } wallets.watcher.add (block); bool error (wallets.node.process_local (block).code != nano::process_result::progress); @@ -1027,7 +1027,7 @@ std::shared_ptr nano::wallet::change_action (nano::account const & if (nano::work_validate (*block)) { wallets.node.logger.try_log (boost::str (boost::format ("Cached or provided work for block %1% account %2% is invalid, regenerating") % block->hash ().to_string () % source_a.to_account ())); - wallets.node.work_generate_blocking (*block, wallets.node.active.active_difficulty ()); + wallets.node.work_generate_blocking (*block, wallets.node.active.limited_active_difficulty ()); } wallets.watcher.add (block); bool error (wallets.node.process_local (block).code != nano::process_result::progress); @@ -1141,7 +1141,7 @@ std::shared_ptr nano::wallet::send_action (nano::account const & so if (nano::work_validate (*block)) { wallets.node.logger.try_log (boost::str (boost::format ("Cached or provided work for block %1% account %2% is invalid, regenerating") % block->hash ().to_string () % account_a.to_account ())); - wallets.node.work_generate_blocking (*block, wallets.node.active.active_difficulty ()); + wallets.node.work_generate_blocking (*block, wallets.node.active.limited_active_difficulty ()); } wallets.watcher.add (block); error = (wallets.node.process_local (block).code != nano::process_result::progress); @@ -1470,7 +1470,8 @@ void nano::work_watcher::run () root = i.second->root (); nano::work_validate (root, i.second->block_work (), &difficulty); } - if (node.active.active_difficulty () > difficulty && i.second != nullptr) + auto active_difficulty (node.active.limited_active_difficulty ()); + if (active_difficulty > difficulty && i.second != nullptr) { auto qualified_root = i.second->qualified_root (); auto hash = i.second->hash (); @@ -1478,7 +1479,7 @@ void nano::work_watcher::run () std::error_code ec; builder.from (*i.second); lock.unlock (); - builder.work (node.work_generate_blocking (root, node.active.active_difficulty ())); + builder.work (node.work_generate_blocking (root, active_difficulty)); std::shared_ptr block (builder.build (ec)); if (!ec) { diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 7bf2624e41..17b7b1e14d 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -2623,8 +2623,10 @@ TEST (rpc, work_generate) TEST (rpc, work_generate_difficulty) { - nano::system system (24000, 1); - auto node (system.nodes[0]); + nano::system system; + nano::node_config node_config (24000, system.logging); + node_config.max_work_generate_difficulty = 0xffff000000000000; + auto node = system.add_node (node_config); scoped_io_thread_name_change scoped_thread_name_io; enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); nano::node_rpc_config node_rpc_config; @@ -2679,7 +2681,7 @@ TEST (rpc, work_generate_difficulty) ASSERT_GE (result_difficulty, difficulty); } { - uint64_t difficulty (node_rpc_config.max_work_generate_difficulty + 1); + uint64_t difficulty (node->config.max_work_generate_difficulty + 1); request.put ("difficulty", nano::to_string_hex (difficulty)); test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s); @@ -2695,8 +2697,10 @@ TEST (rpc, work_generate_difficulty) TEST (rpc, work_generate_multiplier) { - nano::system system (24000, 1); - auto node (system.nodes[0]); + nano::system system; + nano::node_config node_config (24000, system.logging); + node_config.max_work_generate_difficulty = 0xffff000000000000; + auto node = system.add_node (node_config); scoped_io_thread_name_change scoped_thread_name_io; enable_ipc_transport_tcp (node->config.ipc_config.transport_tcp); nano::node_rpc_config node_rpc_config; @@ -2747,7 +2751,7 @@ TEST (rpc, work_generate_multiplier) ASSERT_EQ (response.json.get ("error"), ec.message ()); } { - double max_multiplier (nano::difficulty::to_multiplier (node_rpc_config.max_work_generate_difficulty, node->network_params.network.publish_threshold)); + double max_multiplier (nano::difficulty::to_multiplier (node->config.max_work_generate_difficulty, node->network_params.network.publish_threshold)); request.put ("multiplier", max_multiplier + 1); test_response response (request, rpc.config.port, system.io_ctx); system.deadline_set (5s);