From 3ba48bbd356134bc7684744b979a84a3b1462153 Mon Sep 17 00:00:00 2001 From: Guilherme Lawless Date: Wed, 4 Mar 2020 18:20:45 +0000 Subject: [PATCH] Election refactor follow up (#2619) * active_transactions include cleanup * Allocate a new solicitor on every confirmation request loop * Increment election confirmation_request_count This was accidentally erased in the refactor and there was no test to ensure it. One test removed as it was not testing the intended functionality. Other tests were updated to ensure confirmation_request_count is incremented. It was necessary to disable the rep crawler to properly test the confirmation loop. * Ensuring confirmed elections are not returned in RPC confirmation_info * Adding information on confirmed blocks for RPC confirmation_active * Moving election status definitions to secure/common * Re-lock after activating dependencies (bug found by @cryptocode) Otherwise, could call state_change to `expired_unconfirmed` without owning the mutex * Enhance node.activate_dependencies test by ensuring full confirmation (Serg review) --- nano/core_test/active_transactions.cpp | 113 +++++++++++++--------- nano/core_test/confirmation_solicitor.cpp | 53 +++++----- nano/node/active_transactions.cpp | 8 +- nano/node/active_transactions.hpp | 7 -- nano/node/blockprocessor.cpp | 1 + nano/node/election.cpp | 22 +++-- nano/node/election.hpp | 27 +----- nano/node/json_handler.cpp | 24 +++-- nano/node/node.hpp | 1 + nano/node/repcrawler.hpp | 3 + nano/node/websocket.cpp | 3 +- nano/rpc_test/rpc.cpp | 93 ++++++++++++++++++ nano/secure/common.hpp | 24 +++++ 13 files changed, 257 insertions(+), 122 deletions(-) diff --git a/nano/core_test/active_transactions.cpp b/nano/core_test/active_transactions.cpp index b9b6379f..4daf82e0 100644 --- a/nano/core_test/active_transactions.cpp +++ b/nano/core_test/active_transactions.cpp @@ -7,19 +7,23 @@ using namespace std::chrono_literals; +namespace nano +{ TEST (active_transactions, confirm_active) { - nano::system system (1); - auto & node1 = *system.nodes[0]; - // Send and vote for a block before peering with node2 - system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); - auto send (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); - system.deadline_set (5s); - while (!node1.active.empty () || !node1.block_confirmed_or_being_confirmed (node1.store.tx_begin_read (), send->hash ())) - { - ASSERT_NO_ERROR (system.poll ()); - } - auto & node2 = *system.add_node (nano::node_config (nano::get_available_port (), system.logging)); + nano::system system; + nano::node_flags node_flags; + node_flags.disable_request_loop = true; + auto & node1 = *system.add_node (node_flags); + nano::genesis genesis; + auto send (std::make_shared (genesis.hash (), nano::public_key (), nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (genesis.hash ()))); + ASSERT_EQ (nano::process_result::progress, node1.process (*send).code); + nano::node_config node_config2 (nano::get_available_port (), system.logging); + node_config2.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; + nano::node_flags node_flags2; + // The rep crawler would otherwise request confirmations in order to find representatives + node_flags2.disable_rep_crawler = true; + auto & node2 = *system.add_node (node_config2, node_flags2); system.deadline_set (5s); // Let node2 know about the block while (node2.active.empty ()) @@ -27,56 +31,69 @@ TEST (active_transactions, confirm_active) node1.network.flood_block (send, nano::buffer_drop_policy::no_limiter_drop); ASSERT_NO_ERROR (system.poll ()); } - while (node2.ledger.cache.cemented_count < 2 || !node2.active.empty ()) - { - ASSERT_NO_ERROR (system.poll ()); - } -} - -TEST (active_transactions, confirm_frontier) -{ - nano::system system (1); - auto & node1 = *system.nodes[0]; - // Send and vote for a block before peering with node2 + // Save election to check request count afterwards + auto election = node2.active.election (send->qualified_root ()); + ASSERT_NE (nullptr, election); + // Add key to node1 system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); - auto send (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); - system.deadline_set (5s); - while (!node1.active.empty () || !node1.block_confirmed_or_being_confirmed (node1.store.tx_begin_read (), send->hash ())) + // Add representative to disabled rep crawler + auto peers (node2.network.random_set (1)); + ASSERT_FALSE (peers.empty ()); { - ASSERT_NO_ERROR (system.poll ()); + nano::lock_guard guard (node2.rep_crawler.probable_reps_mutex); + node2.rep_crawler.probable_reps.emplace (nano::test_genesis_key.pub, nano::genesis_amount, *peers.begin ()); } - auto & node2 = *system.add_node (nano::node_config (nano::get_available_port (), system.logging)); - ASSERT_EQ (nano::process_result::progress, node2.process (*send).code); - system.deadline_set (5s); while (node2.ledger.cache.cemented_count < 2 || !node2.active.empty ()) { ASSERT_NO_ERROR (system.poll ()); } + // At least one confirmation request + ASSERT_GT (election->confirmation_request_count, 0); + // Blocks were cleared (except for not_an_account) + ASSERT_EQ (1, election->blocks.size ()); +} } -TEST (active_transactions, confirm_dependent) +namespace nano +{ +TEST (active_transactions, confirm_frontier) { nano::system system; nano::node_flags node_flags; node_flags.disable_request_loop = true; auto & node1 = *system.add_node (node_flags); - system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); - auto send1 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); - auto send2 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); - auto send3 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ())); - nano::node_config node_config; - node_config.peering_port = nano::get_available_port (); - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; - auto & node2 = *system.add_node (node_config); - node2.process_local (send1); - node2.process_local (send2); - node2.process_active (send3); + nano::genesis genesis; + auto send (std::make_shared (genesis.hash (), nano::public_key (), nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (genesis.hash ()))); + ASSERT_EQ (nano::process_result::progress, node1.process (*send).code); + nano::node_flags node_flags2; + // The rep crawler would otherwise request confirmations in order to find representatives + node_flags2.disable_rep_crawler = true; + auto & node2 = *system.add_node (node_flags2); + ASSERT_EQ (nano::process_result::progress, node2.process (*send).code); system.deadline_set (5s); - while (!node2.active.empty ()) + while (node2.active.empty ()) { ASSERT_NO_ERROR (system.poll ()); } - ASSERT_EQ (4, node2.ledger.cache.cemented_count); + // Save election to check request count afterwards + auto election = node2.active.election (send->qualified_root ()); + ASSERT_NE (nullptr, election); + // Add key to node1 + system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); + // Add representative to disabled rep crawler + auto peers (node2.network.random_set (1)); + ASSERT_FALSE (peers.empty ()); + { + nano::lock_guard guard (node2.rep_crawler.probable_reps_mutex); + node2.rep_crawler.probable_reps.emplace (nano::test_genesis_key.pub, nano::genesis_amount, *peers.begin ()); + } + system.deadline_set (5s); + while (node2.ledger.cache.cemented_count < 2 || !node2.active.empty ()) + { + ASSERT_NO_ERROR (system.poll ()); + } + ASSERT_GT (election->confirmation_request_count, 0); +} } TEST (active_transactions, adjusted_difficulty_priority) @@ -694,7 +711,6 @@ TEST (active_transactions, activate_dependencies) system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv); nano::genesis genesis; nano::block_builder builder; - system.deadline_set (std::chrono::seconds (15)); std::shared_ptr block0 = builder.state () .account (nano::test_genesis_key.pub) .previous (genesis.hash ()) @@ -707,6 +723,7 @@ TEST (active_transactions, activate_dependencies) // Establish a representative node2->process_active (block0); node2->block_processor.flush (); + system.deadline_set (10s); while (node1->block (block0->hash ()) == nullptr) { ASSERT_NO_ERROR (system.poll ()); @@ -735,9 +752,17 @@ TEST (active_transactions, activate_dependencies) .build (); node2->process_active (block2); node2->block_processor.flush (); + system.deadline_set (10s); while (node1->block (block2->hash ()) == nullptr) { ASSERT_NO_ERROR (system.poll ()); } ASSERT_NE (nullptr, node1->block (block2->hash ())); + system.deadline_set (10s); + while (!node1->active.empty () || !node2->active.empty ()) + { + ASSERT_NO_ERROR (system.poll ()); + } + ASSERT_TRUE (node1->ledger.block_confirmed (node1->store.tx_begin_read (), block2->hash ())); + ASSERT_TRUE (node2->ledger.block_confirmed (node2->store.tx_begin_read (), block2->hash ())); } diff --git a/nano/core_test/confirmation_solicitor.cpp b/nano/core_test/confirmation_solicitor.cpp index d2595da0..0e6caa3d 100644 --- a/nano/core_test/confirmation_solicitor.cpp +++ b/nano/core_test/confirmation_solicitor.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -9,51 +10,49 @@ using namespace std::chrono_literals; TEST (confirmation_solicitor, batches) { nano::system system; - nano::node_config node_config (nano::get_available_port (), system.logging); - node_config.enable_voting = false; - node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled; nano::node_flags node_flags; - node_flags.disable_udp = false; - auto & node1 = *system.add_node (node_config, node_flags); - node_config.peering_port = nano::get_available_port (); - // To prevent races on the solicitor node_flags.disable_request_loop = true; - auto & node2 = *system.add_node (node_config, node_flags); + node_flags.disable_udp = false; + auto & node1 = *system.add_node (node_flags); + // This tests instantiates a solicitor + node_flags.disable_request_loop = true; + auto & node2 = *system.add_node (node_flags); // Solicitor will only solicit from this representative auto channel1 (node2.network.udp_channels.create (node1.network.endpoint ())); nano::representative representative (nano::test_genesis_key.pub, nano::genesis_amount, channel1); - // Lock active_transactions which uses the solicitor + + std::vector representatives{ representative }; + nano::confirmation_solicitor solicitor (node2.network, node2.network_params.network); + solicitor.prepare (representatives); + // Ensure the representatives are correct + ASSERT_EQ (1, representatives.size ()); + ASSERT_EQ (channel1, representatives.front ().channel); + ASSERT_EQ (nano::test_genesis_key.pub, representatives.front ().account); + auto send (std::make_shared (nano::genesis_hash, nano::keypair ().pub, nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (nano::genesis_hash))); { - nano::lock_guard active_guard (node2.active.mutex); - std::vector representatives{ representative }; - node2.active.solicitor.prepare (representatives); - // Ensure the representatives are correct - ASSERT_EQ (1, representatives.size ()); - ASSERT_EQ (channel1, representatives.front ().channel); - ASSERT_EQ (nano::test_genesis_key.pub, representatives.front ().account); - auto send (std::make_shared (nano::genesis_hash, nano::keypair ().pub, nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (nano::genesis_hash))); + nano::lock_guard guard (node2.active.mutex); for (size_t i (0); i < nano::network::confirm_req_hashes_max; ++i) { auto election (std::make_shared (node2, send, nullptr)); - ASSERT_FALSE (node2.active.solicitor.add (*election)); + ASSERT_FALSE (solicitor.add (*election)); } - ASSERT_EQ (1, node2.active.solicitor.max_confirm_req_batches); + ASSERT_EQ (1, solicitor.max_confirm_req_batches); // Reached the maximum amount of requests for the channel auto election (std::make_shared (node2, send, nullptr)); - ASSERT_TRUE (node2.active.solicitor.add (*election)); + ASSERT_TRUE (solicitor.add (*election)); // Broadcasting should be immediate ASSERT_EQ (0, node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::out)); - ASSERT_FALSE (node2.active.solicitor.broadcast (*election)); - system.deadline_set (5s); - while (node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::out) < 1) - { - ASSERT_NO_ERROR (system.poll ()); - } + ASSERT_FALSE (solicitor.broadcast (*election)); + } + system.deadline_set (5s); + while (node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::out) < 1) + { + ASSERT_NO_ERROR (system.poll ()); } // From rep crawler ASSERT_EQ (1, node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out)); system.deadline_set (5s); - node2.active.solicitor.flush (); + solicitor.flush (); while (node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out) < 2) { ASSERT_NO_ERROR (system.poll ()); diff --git a/nano/node/active_transactions.cpp b/nano/node/active_transactions.cpp index 34479555..a1fc3298 100644 --- a/nano/node/active_transactions.cpp +++ b/nano/node/active_transactions.cpp @@ -1,8 +1,11 @@ #include #include #include +#include #include #include +#include +#include #include #include @@ -16,7 +19,6 @@ confirmation_height_processor (confirmation_height_processor_a), node (node_a), multipliers_cb (20, 1.), trended_active_difficulty (node_a.network_params.network.publish_threshold), -solicitor (node_a.network, node_a.network_params.network), election_time_to_live (node_a.network_params.network.is_test_network () ? 0s : 2s), thread ([this]() { nano::thread_role::set (nano::thread_role::name::request_loop); @@ -225,6 +227,7 @@ void nano::active_transactions::request_confirm (nano::unique_lock & // Only representatives ready to receive batched confirm_req lock_a.unlock (); + nano::confirmation_solicitor solicitor (node.network, node.network_params.network); solicitor.prepare (node.rep_crawler.representatives (node.network_params.protocol.tcp_realtime_protocol_version_min)); lock_a.lock (); @@ -244,7 +247,8 @@ void nano::active_transactions::request_confirm (nano::unique_lock & for (auto i = sorted_roots_l.begin (), n = sorted_roots_l.end (); i != n; ++count_l) { auto & election_l (i->election); - if ((count_l >= node.config.active_elections_size && election_l->election_start < election_ttl_cutoff_l && !node.wallets.watcher->is_watched (i->root)) || election_l->transition_time (saturated_l)) + bool const overflow_l (count_l >= node.config.active_elections_size && election_l->election_start < election_ttl_cutoff_l && !node.wallets.watcher->is_watched (i->root)); + if (overflow_l || election_l->transition_time (solicitor, saturated_l)) { election_l->clear_blocks (); i = sorted_roots_l.erase (i); diff --git a/nano/node/active_transactions.hpp b/nano/node/active_transactions.hpp index c6166ab2..6a5b58c0 100644 --- a/nano/node/active_transactions.hpp +++ b/nano/node/active_transactions.hpp @@ -1,12 +1,6 @@ #pragma once #include -#include -#include -#include -#include -#include -#include #include #include @@ -142,7 +136,6 @@ public: size_t inactive_votes_cache_size (); size_t election_winner_details_size (); void add_election_winner_details (nano::block_hash const &, std::shared_ptr const &); - nano::confirmation_solicitor solicitor; private: std::mutex election_winner_details_mutex; diff --git a/nano/node/blockprocessor.cpp b/nano/node/blockprocessor.cpp index 77f84a63..5447645f 100644 --- a/nano/node/blockprocessor.cpp +++ b/nano/node/blockprocessor.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include diff --git a/nano/node/election.cpp b/nano/node/election.cpp index 5e52ed6d..4997d741 100644 --- a/nano/node/election.cpp +++ b/nano/node/election.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -148,13 +149,14 @@ bool nano::election::state_change (nano::election::state_t expected_a, nano::ele return result; } -void nano::election::send_confirm_req () +void nano::election::send_confirm_req (nano::confirmation_solicitor & solicitor_a) { if (last_req + std::chrono::seconds (15) < std::chrono::steady_clock::now ()) { - if (!node.active.solicitor.add (*this)) + if (!solicitor_a.add (*this)) { last_req = std::chrono::steady_clock::now (); + ++confirmation_request_count; } } } @@ -242,18 +244,18 @@ void nano::election::activate_dependencies () } } -void nano::election::broadcast_block () +void nano::election::broadcast_block (nano::confirmation_solicitor & solicitor_a) { if (base_latency () * 5 < std::chrono::steady_clock::now () - last_block) { - if (!node.active.solicitor.broadcast (*this)) + if (!solicitor_a.broadcast (*this)) { last_block = std::chrono::steady_clock::now (); } } } -bool nano::election::transition_time (bool const saturated_a) +bool nano::election::transition_time (nano::confirmation_solicitor & solicitor_a, bool const saturated_a) { debug_assert (!node.active.mutex.try_lock ()); nano::unique_lock lock (timepoints_mutex); @@ -271,18 +273,19 @@ bool nano::election::transition_time (bool const saturated_a) break; } case nano::election::state_t::active: - broadcast_block (); - send_confirm_req (); + broadcast_block (solicitor_a); + send_confirm_req (solicitor_a); if (base_latency () * active_duration_factor < std::chrono::steady_clock::now () - state_start) { state_change (nano::election::state_t::active, nano::election::state_t::backtracking); lock.unlock (); activate_dependencies (); + lock.lock (); } break; case nano::election::state_t::backtracking: - broadcast_block (); - send_confirm_req (); + broadcast_block (solicitor_a); + send_confirm_req (solicitor_a); break; case nano::election::state_t::confirmed: if (base_latency () * (saturated_a ? confirmed_duration_factor_saturated : confirmed_duration_factor) < std::chrono::steady_clock::now () - state_start) @@ -296,7 +299,6 @@ bool nano::election::transition_time (bool const saturated_a) debug_assert (false); break; } - // Note: lock (timepoints_mutex) is at an unknown state here - possibly unlocked before activate_dependencies if (!confirmed () && std::chrono::minutes (5) < std::chrono::steady_clock::now () - election_start) { result = true; diff --git a/nano/node/election.hpp b/nano/node/election.hpp index 3205345f..9339cc2b 100644 --- a/nano/node/election.hpp +++ b/nano/node/election.hpp @@ -12,27 +12,8 @@ namespace nano { class channel; +class confirmation_solicitor; class node; -enum class election_status_type : uint8_t -{ - ongoing = 0, - active_confirmed_quorum = 1, - active_confirmation_height = 2, - inactive_confirmation_height = 3, - stopped = 5 -}; -class election_status final -{ -public: - std::shared_ptr winner; - nano::amount tally; - std::chrono::milliseconds election_end; - std::chrono::milliseconds election_duration; - unsigned confirmation_request_count; - unsigned block_count; - unsigned voter_count; - election_status_type type; -}; class vote_info final { public: @@ -80,8 +61,8 @@ private: // State management bool valid_change (nano::election::state_t, nano::election::state_t) const; bool state_change (nano::election::state_t, nano::election::state_t); - void broadcast_block (); - void send_confirm_req (); + void broadcast_block (nano::confirmation_solicitor &); + void send_confirm_req (nano::confirmation_solicitor &); void activate_dependencies (); public: @@ -101,7 +82,7 @@ public: void insert_inactive_votes_cache (nano::block_hash const &); public: // State transitions - bool transition_time (bool const saturated); + bool transition_time (nano::confirmation_solicitor &, bool const saturated); void transition_passive (); void transition_active (); diff --git a/nano/node/json_handler.cpp b/nano/node/json_handler.cpp index a43e23f2..e05e32df 100644 --- a/nano/node/json_handler.cpp +++ b/nano/node/json_handler.cpp @@ -1754,6 +1754,7 @@ void nano::json_handler::chain (bool successors) void nano::json_handler::confirmation_active () { uint64_t announcements (0); + uint64_t confirmed (0); boost::optional announcements_text (request.get_optional ("announcements")); if (announcements_text.is_initialized ()) { @@ -1764,15 +1765,24 @@ void nano::json_handler::confirmation_active () nano::lock_guard lock (node.active.mutex); for (auto i (node.active.roots.begin ()), n (node.active.roots.end ()); i != n; ++i) { - if (i->election->confirmation_request_count >= announcements && !i->election->confirmed ()) + if (i->election->confirmation_request_count >= announcements) { - boost::property_tree::ptree entry; - entry.put ("", i->root.to_string ()); - elections.push_back (std::make_pair ("", entry)); + if (!i->election->confirmed ()) + { + boost::property_tree::ptree entry; + entry.put ("", i->root.to_string ()); + elections.push_back (std::make_pair ("", entry)); + } + else + { + ++confirmed; + } } } } response_l.add_child ("confirmations", elections); + response_l.put ("unconfirmed", elections.size ()); + response_l.put ("confirmed", confirmed); response_errors (); } @@ -1840,11 +1850,9 @@ void nano::json_handler::confirmation_info () nano::qualified_root root; if (!root.decode_hex (root_text)) { - nano::lock_guard lock (node.active.mutex); - auto conflict_info (node.active.roots.find (root)); - if (conflict_info != node.active.roots.end ()) + auto election (node.active.election (root)); + if (election != nullptr && !election->confirmed ()) { - auto election (conflict_info->election); response_l.put ("announcements", std::to_string (election->confirmation_request_count)); response_l.put ("voters", std::to_string (election->last_votes.size ())); response_l.put ("last_winner", election->status.winner->hash ().to_string ()); diff --git a/nano/node/node.hpp b/nano/node/node.hpp index 6dd5860c..61964696 100644 --- a/nano/node/node.hpp +++ b/nano/node/node.hpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include diff --git a/nano/node/repcrawler.hpp b/nano/node/repcrawler.hpp index 0071584f..06f14910 100644 --- a/nano/node/repcrawler.hpp +++ b/nano/node/repcrawler.hpp @@ -147,6 +147,9 @@ private: /** Probable representatives */ probably_rep_t probable_reps; + friend class active_transactions_confirm_active_Test; + friend class active_transactions_confirm_frontier_Test; + std::deque, std::shared_ptr>> responses; }; } diff --git a/nano/node/websocket.cpp b/nano/node/websocket.cpp index 4cb686e7..9e38c572 100644 --- a/nano/node/websocket.cpp +++ b/nano/node/websocket.cpp @@ -2,7 +2,8 @@ #include #include #include -#include +#include +#include #include #include diff --git a/nano/rpc_test/rpc.cpp b/nano/rpc_test/rpc.cpp index 4431bae8..5323779e 100644 --- a/nano/rpc_test/rpc.cpp +++ b/nano/rpc_test/rpc.cpp @@ -8151,3 +8151,96 @@ TEST (rpc, node_telemetry_self) ASSERT_EQ (std::error_code (nano::error_rpc::peer_not_found).message (), response.json.get ("error")); } } + +TEST (rpc, confirmation_active) +{ + nano::system system; + nano::node_config node_config; + node_config.ipc_config.transport_tcp.enabled = true; + node_config.ipc_config.transport_tcp.port = nano::get_available_port (); + nano::node_flags node_flags; + node_flags.disable_request_loop = true; + auto & node1 (*system.add_node (node_config, node_flags)); + scoped_io_thread_name_change scoped_thread_name_io; + nano::node_rpc_config node_rpc_config; + nano::ipc::ipc_server ipc_server (node1, node_rpc_config); + nano::rpc_config rpc_config (nano::get_available_port (), true); + rpc_config.rpc_process.ipc_port = node1.config.ipc_config.transport_tcp.port; + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); + rpc.start (); + + nano::genesis genesis; + auto send1 (std::make_shared (genesis.hash (), nano::public_key (), nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (genesis.hash ()))); + auto send2 (std::make_shared (send1->hash (), nano::public_key (), nano::genesis_amount - 200, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (send1->hash ()))); + node1.process_active (send1); + node1.process_active (send2); + node1.block_processor.flush (); + ASSERT_EQ (2, node1.active.size ()); + { + nano::lock_guard guard (node1.active.mutex); + auto info (node1.active.roots.find (send1->qualified_root ())); + ASSERT_NE (node1.active.roots.end (), info); + info->election->confirm_once (); + } + + boost::property_tree::ptree request; + request.put ("action", "confirmation_active"); + { + test_response response (request, rpc.config.port, system.io_ctx); + system.deadline_set (5s); + while (response.status == 0) + { + ASSERT_NO_ERROR (system.poll ()); + } + ASSERT_EQ (200, response.status); + auto & confirmations (response.json.get_child ("confirmations")); + ASSERT_EQ (1, confirmations.size ()); + ASSERT_EQ (send2->qualified_root ().to_string (), confirmations.front ().second.get ("")); + ASSERT_EQ (1, response.json.get ("unconfirmed")); + ASSERT_EQ (1, response.json.get ("confirmed")); + } +} + +TEST (rpc, confirmation_info) +{ + nano::system system; + auto & node1 = *add_ipc_enabled_node (system); + scoped_io_thread_name_change scoped_thread_name_io; + nano::node_rpc_config node_rpc_config; + nano::ipc::ipc_server ipc_server (node1, node_rpc_config); + nano::rpc_config rpc_config (nano::get_available_port (), true); + rpc_config.rpc_process.ipc_port = node1.config.ipc_config.transport_tcp.port; + nano::ipc_rpc_processor ipc_rpc_processor (system.io_ctx, rpc_config); + nano::rpc rpc (system.io_ctx, rpc_config, ipc_rpc_processor); + rpc.start (); + + nano::genesis genesis; + auto send (std::make_shared (genesis.hash (), nano::public_key (), nano::genesis_amount - 100, nano::test_genesis_key.prv, nano::test_genesis_key.pub, *system.work.generate (genesis.hash ()))); + node1.process_active (send); + node1.block_processor.flush (); + ASSERT_FALSE (node1.active.empty ()); + + boost::property_tree::ptree request; + request.put ("action", "confirmation_info"); + request.put ("root", send->qualified_root ().to_string ()); + request.put ("representatives", "true"); + request.put ("json_block", "true"); + { + test_response response (request, rpc.config.port, system.io_ctx); + system.deadline_set (5s); + while (response.status == 0) + { + ASSERT_NO_ERROR (system.poll ()); + } + ASSERT_EQ (200, response.status); + ASSERT_EQ (1, response.json.count ("announcements")); + ASSERT_EQ (1, response.json.get ("voters")); + ASSERT_EQ (send->hash ().to_string (), response.json.get ("last_winner")); + auto & blocks (response.json.get_child ("blocks")); + ASSERT_EQ (1, blocks.size ()); + auto & representatives (blocks.front ().second.get_child ("representatives")); + ASSERT_EQ (1, representatives.size ()); + ASSERT_EQ (0, response.json.get ("total_tally")); + } +} diff --git a/nano/secure/common.hpp b/nano/secure/common.hpp index ea5a43ee..be6e8749 100644 --- a/nano/secure/common.hpp +++ b/nano/secure/common.hpp @@ -506,5 +506,29 @@ public: std::atomic account_count{ 0 }; }; +/* Defines the possible states for an election to stop in */ +enum class election_status_type : uint8_t +{ + ongoing = 0, + active_confirmed_quorum = 1, + active_confirmation_height = 2, + inactive_confirmation_height = 3, + stopped = 5 +}; + +/* Holds a summary of an election */ +class election_status final +{ +public: + std::shared_ptr winner; + nano::amount tally; + std::chrono::milliseconds election_end; + std::chrono::milliseconds election_duration; + unsigned confirmation_request_count; + unsigned block_count; + unsigned voter_count; + election_status_type type; +}; + nano::wallet_id random_wallet_id (); }