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)
This commit is contained in:
Guilherme Lawless 2020-03-04 18:20:45 +00:00 committed by GitHub
commit 3ba48bbd35
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 257 additions and 122 deletions

View file

@ -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<nano::send_block> (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<std::mutex> 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<nano::send_block> (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<std::mutex> 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<nano::block> 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 ()));
}

View file

@ -1,5 +1,6 @@
#include <nano/core_test/testutil.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/node/confirmation_solicitor.hpp>
#include <nano/node/testing.hpp>
#include <gtest/gtest.h>
@ -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<nano::representative> 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::send_block> (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<std::mutex> active_guard (node2.active.mutex);
std::vector<nano::representative> 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::send_block> (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<std::mutex> guard (node2.active.mutex);
for (size_t i (0); i < nano::network::confirm_req_hashes_max; ++i)
{
auto election (std::make_shared<nano::election> (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<nano::election> (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 ());

View file

@ -1,8 +1,11 @@
#include <nano/lib/threading.hpp>
#include <nano/node/active_transactions.hpp>
#include <nano/node/confirmation_height_processor.hpp>
#include <nano/node/confirmation_solicitor.hpp>
#include <nano/node/election.hpp>
#include <nano/node/node.hpp>
#include <nano/node/repcrawler.hpp>
#include <nano/secure/blockstore.hpp>
#include <boost/format.hpp>
#include <boost/variant/get.hpp>
@ -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<std::mutex> &
// 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<std::mutex> &
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);

View file

@ -1,12 +1,6 @@
#pragma once
#include <nano/lib/numbers.hpp>
#include <nano/node/confirmation_solicitor.hpp>
#include <nano/node/election.hpp>
#include <nano/node/gap_cache.hpp>
#include <nano/node/repcrawler.hpp>
#include <nano/node/transport/transport.hpp>
#include <nano/secure/blockstore.hpp>
#include <nano/secure/common.hpp>
#include <boost/circular_buffer.hpp>
@ -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<nano::election> const &);
nano::confirmation_solicitor solicitor;
private:
std::mutex election_winner_details_mutex;

View file

@ -1,5 +1,6 @@
#include <nano/lib/timer.hpp>
#include <nano/node/blockprocessor.hpp>
#include <nano/node/election.hpp>
#include <nano/node/node.hpp>
#include <nano/secure/blockstore.hpp>

View file

@ -1,3 +1,4 @@
#include <nano/node/confirmation_solicitor.hpp>
#include <nano/node/election.hpp>
#include <nano/node/node.hpp>
@ -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<std::mutex> 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;

View file

@ -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<nano::block> 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 ();

View file

@ -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<std::string> announcements_text (request.get_optional<std::string> ("announcements"));
if (announcements_text.is_initialized ())
{
@ -1764,15 +1765,24 @@ void nano::json_handler::confirmation_active ()
nano::lock_guard<std::mutex> 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<std::mutex> 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 ());

View file

@ -11,6 +11,7 @@
#include <nano/node/bootstrap/bootstrap_server.hpp>
#include <nano/node/confirmation_height_processor.hpp>
#include <nano/node/distributed_work_factory.hpp>
#include <nano/node/election.hpp>
#include <nano/node/gap_cache.hpp>
#include <nano/node/logging.hpp>
#include <nano/node/network.hpp>

View file

@ -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::pair<std::shared_ptr<nano::transport::channel>, std::shared_ptr<nano::vote>>> responses;
};
}

View file

@ -2,7 +2,8 @@
#include <nano/boost/asio/dispatch.hpp>
#include <nano/boost/asio/strand.hpp>
#include <nano/lib/work.hpp>
#include <nano/node/active_transactions.hpp>
#include <nano/node/election.hpp>
#include <nano/node/transport/transport.hpp>
#include <nano/node/wallet.hpp>
#include <nano/node/websocket.hpp>

View file

@ -8151,3 +8151,96 @@ TEST (rpc, node_telemetry_self)
ASSERT_EQ (std::error_code (nano::error_rpc::peer_not_found).message (), response.json.get<std::string> ("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<nano::send_block> (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<nano::send_block> (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<std::mutex> 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<std::string> (""));
ASSERT_EQ (1, response.json.get<unsigned> ("unconfirmed"));
ASSERT_EQ (1, response.json.get<unsigned> ("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<nano::send_block> (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<unsigned> ("voters"));
ASSERT_EQ (send->hash ().to_string (), response.json.get<std::string> ("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<unsigned> ("total_tally"));
}
}

View file

@ -506,5 +506,29 @@ public:
std::atomic<uint64_t> 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<nano::block> 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 ();
}