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 (); }