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:
parent
d2cbf2a57e
commit
3ba48bbd35
13 changed files with 257 additions and 122 deletions
|
|
@ -7,19 +7,23 @@
|
||||||
|
|
||||||
using namespace std::chrono_literals;
|
using namespace std::chrono_literals;
|
||||||
|
|
||||||
|
namespace nano
|
||||||
|
{
|
||||||
TEST (active_transactions, confirm_active)
|
TEST (active_transactions, confirm_active)
|
||||||
{
|
{
|
||||||
nano::system system (1);
|
nano::system system;
|
||||||
auto & node1 = *system.nodes[0];
|
nano::node_flags node_flags;
|
||||||
// Send and vote for a block before peering with node2
|
node_flags.disable_request_loop = true;
|
||||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
auto & node1 = *system.add_node (node_flags);
|
||||||
auto send (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ()));
|
nano::genesis genesis;
|
||||||
system.deadline_set (5s);
|
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 ())));
|
||||||
while (!node1.active.empty () || !node1.block_confirmed_or_being_confirmed (node1.store.tx_begin_read (), send->hash ()))
|
ASSERT_EQ (nano::process_result::progress, node1.process (*send).code);
|
||||||
{
|
nano::node_config node_config2 (nano::get_available_port (), system.logging);
|
||||||
ASSERT_NO_ERROR (system.poll ());
|
node_config2.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
||||||
}
|
nano::node_flags node_flags2;
|
||||||
auto & node2 = *system.add_node (nano::node_config (nano::get_available_port (), system.logging));
|
// 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);
|
system.deadline_set (5s);
|
||||||
// Let node2 know about the block
|
// Let node2 know about the block
|
||||||
while (node2.active.empty ())
|
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);
|
node1.network.flood_block (send, nano::buffer_drop_policy::no_limiter_drop);
|
||||||
ASSERT_NO_ERROR (system.poll ());
|
ASSERT_NO_ERROR (system.poll ());
|
||||||
}
|
}
|
||||||
while (node2.ledger.cache.cemented_count < 2 || !node2.active.empty ())
|
// Save election to check request count afterwards
|
||||||
{
|
auto election = node2.active.election (send->qualified_root ());
|
||||||
ASSERT_NO_ERROR (system.poll ());
|
ASSERT_NE (nullptr, election);
|
||||||
}
|
// Add key to node1
|
||||||
}
|
|
||||||
|
|
||||||
TEST (active_transactions, confirm_frontier)
|
|
||||||
{
|
|
||||||
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);
|
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 ()));
|
// Add representative to disabled rep crawler
|
||||||
system.deadline_set (5s);
|
auto peers (node2.network.random_set (1));
|
||||||
while (!node1.active.empty () || !node1.block_confirmed_or_being_confirmed (node1.store.tx_begin_read (), send->hash ()))
|
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 ())
|
while (node2.ledger.cache.cemented_count < 2 || !node2.active.empty ())
|
||||||
{
|
{
|
||||||
ASSERT_NO_ERROR (system.poll ());
|
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::system system;
|
||||||
nano::node_flags node_flags;
|
nano::node_flags node_flags;
|
||||||
node_flags.disable_request_loop = true;
|
node_flags.disable_request_loop = true;
|
||||||
auto & node1 = *system.add_node (node_flags);
|
auto & node1 = *system.add_node (node_flags);
|
||||||
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
nano::genesis genesis;
|
||||||
auto send1 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ()));
|
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 ())));
|
||||||
auto send2 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ()));
|
ASSERT_EQ (nano::process_result::progress, node1.process (*send).code);
|
||||||
auto send3 (system.wallet (0)->send_action (nano::test_genesis_key.pub, nano::public_key (), node1.config.receive_minimum.number ()));
|
nano::node_flags node_flags2;
|
||||||
nano::node_config node_config;
|
// The rep crawler would otherwise request confirmations in order to find representatives
|
||||||
node_config.peering_port = nano::get_available_port ();
|
node_flags2.disable_rep_crawler = true;
|
||||||
node_config.frontiers_confirmation = nano::frontiers_confirmation_mode::disabled;
|
auto & node2 = *system.add_node (node_flags2);
|
||||||
auto & node2 = *system.add_node (node_config);
|
ASSERT_EQ (nano::process_result::progress, node2.process (*send).code);
|
||||||
node2.process_local (send1);
|
|
||||||
node2.process_local (send2);
|
|
||||||
node2.process_active (send3);
|
|
||||||
system.deadline_set (5s);
|
system.deadline_set (5s);
|
||||||
while (!node2.active.empty ())
|
while (node2.active.empty ())
|
||||||
{
|
{
|
||||||
ASSERT_NO_ERROR (system.poll ());
|
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)
|
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);
|
system.wallet (0)->insert_adhoc (nano::test_genesis_key.prv);
|
||||||
nano::genesis genesis;
|
nano::genesis genesis;
|
||||||
nano::block_builder builder;
|
nano::block_builder builder;
|
||||||
system.deadline_set (std::chrono::seconds (15));
|
|
||||||
std::shared_ptr<nano::block> block0 = builder.state ()
|
std::shared_ptr<nano::block> block0 = builder.state ()
|
||||||
.account (nano::test_genesis_key.pub)
|
.account (nano::test_genesis_key.pub)
|
||||||
.previous (genesis.hash ())
|
.previous (genesis.hash ())
|
||||||
|
|
@ -707,6 +723,7 @@ TEST (active_transactions, activate_dependencies)
|
||||||
// Establish a representative
|
// Establish a representative
|
||||||
node2->process_active (block0);
|
node2->process_active (block0);
|
||||||
node2->block_processor.flush ();
|
node2->block_processor.flush ();
|
||||||
|
system.deadline_set (10s);
|
||||||
while (node1->block (block0->hash ()) == nullptr)
|
while (node1->block (block0->hash ()) == nullptr)
|
||||||
{
|
{
|
||||||
ASSERT_NO_ERROR (system.poll ());
|
ASSERT_NO_ERROR (system.poll ());
|
||||||
|
|
@ -735,9 +752,17 @@ TEST (active_transactions, activate_dependencies)
|
||||||
.build ();
|
.build ();
|
||||||
node2->process_active (block2);
|
node2->process_active (block2);
|
||||||
node2->block_processor.flush ();
|
node2->block_processor.flush ();
|
||||||
|
system.deadline_set (10s);
|
||||||
while (node1->block (block2->hash ()) == nullptr)
|
while (node1->block (block2->hash ()) == nullptr)
|
||||||
{
|
{
|
||||||
ASSERT_NO_ERROR (system.poll ());
|
ASSERT_NO_ERROR (system.poll ());
|
||||||
}
|
}
|
||||||
ASSERT_NE (nullptr, node1->block (block2->hash ()));
|
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 ()));
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include <nano/core_test/testutil.hpp>
|
#include <nano/core_test/testutil.hpp>
|
||||||
#include <nano/lib/jsonconfig.hpp>
|
#include <nano/lib/jsonconfig.hpp>
|
||||||
|
#include <nano/node/confirmation_solicitor.hpp>
|
||||||
#include <nano/node/testing.hpp>
|
#include <nano/node/testing.hpp>
|
||||||
|
|
||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
@ -9,51 +10,49 @@ using namespace std::chrono_literals;
|
||||||
TEST (confirmation_solicitor, batches)
|
TEST (confirmation_solicitor, batches)
|
||||||
{
|
{
|
||||||
nano::system system;
|
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;
|
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;
|
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
|
// Solicitor will only solicit from this representative
|
||||||
auto channel1 (node2.network.udp_channels.create (node1.network.endpoint ()));
|
auto channel1 (node2.network.udp_channels.create (node1.network.endpoint ()));
|
||||||
nano::representative representative (nano::test_genesis_key.pub, nano::genesis_amount, channel1);
|
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);
|
nano::lock_guard<std::mutex> 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)));
|
|
||||||
for (size_t i (0); i < nano::network::confirm_req_hashes_max; ++i)
|
for (size_t i (0); i < nano::network::confirm_req_hashes_max; ++i)
|
||||||
{
|
{
|
||||||
auto election (std::make_shared<nano::election> (node2, send, nullptr));
|
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
|
// Reached the maximum amount of requests for the channel
|
||||||
auto election (std::make_shared<nano::election> (node2, send, nullptr));
|
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
|
// Broadcasting should be immediate
|
||||||
ASSERT_EQ (0, node2.stats.count (nano::stat::type::message, nano::stat::detail::publish, nano::stat::dir::out));
|
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));
|
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)
|
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_NO_ERROR (system.poll ());
|
||||||
}
|
}
|
||||||
// From rep crawler
|
// From rep crawler
|
||||||
ASSERT_EQ (1, node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out));
|
ASSERT_EQ (1, node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out));
|
||||||
system.deadline_set (5s);
|
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)
|
while (node2.stats.count (nano::stat::type::message, nano::stat::detail::confirm_req, nano::stat::dir::out) < 2)
|
||||||
{
|
{
|
||||||
ASSERT_NO_ERROR (system.poll ());
|
ASSERT_NO_ERROR (system.poll ());
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,11 @@
|
||||||
#include <nano/lib/threading.hpp>
|
#include <nano/lib/threading.hpp>
|
||||||
#include <nano/node/active_transactions.hpp>
|
#include <nano/node/active_transactions.hpp>
|
||||||
#include <nano/node/confirmation_height_processor.hpp>
|
#include <nano/node/confirmation_height_processor.hpp>
|
||||||
|
#include <nano/node/confirmation_solicitor.hpp>
|
||||||
#include <nano/node/election.hpp>
|
#include <nano/node/election.hpp>
|
||||||
#include <nano/node/node.hpp>
|
#include <nano/node/node.hpp>
|
||||||
|
#include <nano/node/repcrawler.hpp>
|
||||||
|
#include <nano/secure/blockstore.hpp>
|
||||||
|
|
||||||
#include <boost/format.hpp>
|
#include <boost/format.hpp>
|
||||||
#include <boost/variant/get.hpp>
|
#include <boost/variant/get.hpp>
|
||||||
|
|
@ -16,7 +19,6 @@ confirmation_height_processor (confirmation_height_processor_a),
|
||||||
node (node_a),
|
node (node_a),
|
||||||
multipliers_cb (20, 1.),
|
multipliers_cb (20, 1.),
|
||||||
trended_active_difficulty (node_a.network_params.network.publish_threshold),
|
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),
|
election_time_to_live (node_a.network_params.network.is_test_network () ? 0s : 2s),
|
||||||
thread ([this]() {
|
thread ([this]() {
|
||||||
nano::thread_role::set (nano::thread_role::name::request_loop);
|
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
|
// Only representatives ready to receive batched confirm_req
|
||||||
lock_a.unlock ();
|
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));
|
solicitor.prepare (node.rep_crawler.representatives (node.network_params.protocol.tcp_realtime_protocol_version_min));
|
||||||
lock_a.lock ();
|
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)
|
for (auto i = sorted_roots_l.begin (), n = sorted_roots_l.end (); i != n; ++count_l)
|
||||||
{
|
{
|
||||||
auto & election_l (i->election);
|
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 ();
|
election_l->clear_blocks ();
|
||||||
i = sorted_roots_l.erase (i);
|
i = sorted_roots_l.erase (i);
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,6 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <nano/lib/numbers.hpp>
|
#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 <nano/secure/common.hpp>
|
||||||
|
|
||||||
#include <boost/circular_buffer.hpp>
|
#include <boost/circular_buffer.hpp>
|
||||||
|
|
@ -142,7 +136,6 @@ public:
|
||||||
size_t inactive_votes_cache_size ();
|
size_t inactive_votes_cache_size ();
|
||||||
size_t election_winner_details_size ();
|
size_t election_winner_details_size ();
|
||||||
void add_election_winner_details (nano::block_hash const &, std::shared_ptr<nano::election> const &);
|
void add_election_winner_details (nano::block_hash const &, std::shared_ptr<nano::election> const &);
|
||||||
nano::confirmation_solicitor solicitor;
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
std::mutex election_winner_details_mutex;
|
std::mutex election_winner_details_mutex;
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include <nano/lib/timer.hpp>
|
#include <nano/lib/timer.hpp>
|
||||||
#include <nano/node/blockprocessor.hpp>
|
#include <nano/node/blockprocessor.hpp>
|
||||||
|
#include <nano/node/election.hpp>
|
||||||
#include <nano/node/node.hpp>
|
#include <nano/node/node.hpp>
|
||||||
#include <nano/secure/blockstore.hpp>
|
#include <nano/secure/blockstore.hpp>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
#include <nano/node/confirmation_solicitor.hpp>
|
||||||
#include <nano/node/election.hpp>
|
#include <nano/node/election.hpp>
|
||||||
#include <nano/node/node.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;
|
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 (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 ();
|
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 (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 ();
|
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 ());
|
debug_assert (!node.active.mutex.try_lock ());
|
||||||
nano::unique_lock<std::mutex> lock (timepoints_mutex);
|
nano::unique_lock<std::mutex> lock (timepoints_mutex);
|
||||||
|
|
@ -271,18 +273,19 @@ bool nano::election::transition_time (bool const saturated_a)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case nano::election::state_t::active:
|
case nano::election::state_t::active:
|
||||||
broadcast_block ();
|
broadcast_block (solicitor_a);
|
||||||
send_confirm_req ();
|
send_confirm_req (solicitor_a);
|
||||||
if (base_latency () * active_duration_factor < std::chrono::steady_clock::now () - state_start)
|
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);
|
state_change (nano::election::state_t::active, nano::election::state_t::backtracking);
|
||||||
lock.unlock ();
|
lock.unlock ();
|
||||||
activate_dependencies ();
|
activate_dependencies ();
|
||||||
|
lock.lock ();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case nano::election::state_t::backtracking:
|
case nano::election::state_t::backtracking:
|
||||||
broadcast_block ();
|
broadcast_block (solicitor_a);
|
||||||
send_confirm_req ();
|
send_confirm_req (solicitor_a);
|
||||||
break;
|
break;
|
||||||
case nano::election::state_t::confirmed:
|
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)
|
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);
|
debug_assert (false);
|
||||||
break;
|
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)
|
if (!confirmed () && std::chrono::minutes (5) < std::chrono::steady_clock::now () - election_start)
|
||||||
{
|
{
|
||||||
result = true;
|
result = true;
|
||||||
|
|
|
||||||
|
|
@ -12,27 +12,8 @@
|
||||||
namespace nano
|
namespace nano
|
||||||
{
|
{
|
||||||
class channel;
|
class channel;
|
||||||
|
class confirmation_solicitor;
|
||||||
class node;
|
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
|
class vote_info final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
@ -80,8 +61,8 @@ private: // State management
|
||||||
|
|
||||||
bool valid_change (nano::election::state_t, nano::election::state_t) const;
|
bool valid_change (nano::election::state_t, nano::election::state_t) const;
|
||||||
bool state_change (nano::election::state_t, nano::election::state_t);
|
bool state_change (nano::election::state_t, nano::election::state_t);
|
||||||
void broadcast_block ();
|
void broadcast_block (nano::confirmation_solicitor &);
|
||||||
void send_confirm_req ();
|
void send_confirm_req (nano::confirmation_solicitor &);
|
||||||
void activate_dependencies ();
|
void activate_dependencies ();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
@ -101,7 +82,7 @@ public:
|
||||||
void insert_inactive_votes_cache (nano::block_hash const &);
|
void insert_inactive_votes_cache (nano::block_hash const &);
|
||||||
|
|
||||||
public: // State transitions
|
public: // State transitions
|
||||||
bool transition_time (bool const saturated);
|
bool transition_time (nano::confirmation_solicitor &, bool const saturated);
|
||||||
void transition_passive ();
|
void transition_passive ();
|
||||||
void transition_active ();
|
void transition_active ();
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1754,6 +1754,7 @@ void nano::json_handler::chain (bool successors)
|
||||||
void nano::json_handler::confirmation_active ()
|
void nano::json_handler::confirmation_active ()
|
||||||
{
|
{
|
||||||
uint64_t announcements (0);
|
uint64_t announcements (0);
|
||||||
|
uint64_t confirmed (0);
|
||||||
boost::optional<std::string> announcements_text (request.get_optional<std::string> ("announcements"));
|
boost::optional<std::string> announcements_text (request.get_optional<std::string> ("announcements"));
|
||||||
if (announcements_text.is_initialized ())
|
if (announcements_text.is_initialized ())
|
||||||
{
|
{
|
||||||
|
|
@ -1764,15 +1765,24 @@ void nano::json_handler::confirmation_active ()
|
||||||
nano::lock_guard<std::mutex> lock (node.active.mutex);
|
nano::lock_guard<std::mutex> lock (node.active.mutex);
|
||||||
for (auto i (node.active.roots.begin ()), n (node.active.roots.end ()); i != n; ++i)
|
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;
|
if (!i->election->confirmed ())
|
||||||
entry.put ("", i->root.to_string ());
|
{
|
||||||
elections.push_back (std::make_pair ("", entry));
|
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.add_child ("confirmations", elections);
|
||||||
|
response_l.put ("unconfirmed", elections.size ());
|
||||||
|
response_l.put ("confirmed", confirmed);
|
||||||
response_errors ();
|
response_errors ();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1840,11 +1850,9 @@ void nano::json_handler::confirmation_info ()
|
||||||
nano::qualified_root root;
|
nano::qualified_root root;
|
||||||
if (!root.decode_hex (root_text))
|
if (!root.decode_hex (root_text))
|
||||||
{
|
{
|
||||||
nano::lock_guard<std::mutex> lock (node.active.mutex);
|
auto election (node.active.election (root));
|
||||||
auto conflict_info (node.active.roots.find (root));
|
if (election != nullptr && !election->confirmed ())
|
||||||
if (conflict_info != node.active.roots.end ())
|
|
||||||
{
|
{
|
||||||
auto election (conflict_info->election);
|
|
||||||
response_l.put ("announcements", std::to_string (election->confirmation_request_count));
|
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 ("voters", std::to_string (election->last_votes.size ()));
|
||||||
response_l.put ("last_winner", election->status.winner->hash ().to_string ());
|
response_l.put ("last_winner", election->status.winner->hash ().to_string ());
|
||||||
|
|
|
||||||
|
|
@ -11,6 +11,7 @@
|
||||||
#include <nano/node/bootstrap/bootstrap_server.hpp>
|
#include <nano/node/bootstrap/bootstrap_server.hpp>
|
||||||
#include <nano/node/confirmation_height_processor.hpp>
|
#include <nano/node/confirmation_height_processor.hpp>
|
||||||
#include <nano/node/distributed_work_factory.hpp>
|
#include <nano/node/distributed_work_factory.hpp>
|
||||||
|
#include <nano/node/election.hpp>
|
||||||
#include <nano/node/gap_cache.hpp>
|
#include <nano/node/gap_cache.hpp>
|
||||||
#include <nano/node/logging.hpp>
|
#include <nano/node/logging.hpp>
|
||||||
#include <nano/node/network.hpp>
|
#include <nano/node/network.hpp>
|
||||||
|
|
|
||||||
|
|
@ -147,6 +147,9 @@ private:
|
||||||
/** Probable representatives */
|
/** Probable representatives */
|
||||||
probably_rep_t probable_reps;
|
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;
|
std::deque<std::pair<std::shared_ptr<nano::transport::channel>, std::shared_ptr<nano::vote>>> responses;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,8 @@
|
||||||
#include <nano/boost/asio/dispatch.hpp>
|
#include <nano/boost/asio/dispatch.hpp>
|
||||||
#include <nano/boost/asio/strand.hpp>
|
#include <nano/boost/asio/strand.hpp>
|
||||||
#include <nano/lib/work.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/wallet.hpp>
|
||||||
#include <nano/node/websocket.hpp>
|
#include <nano/node/websocket.hpp>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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"));
|
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"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -506,5 +506,29 @@ public:
|
||||||
std::atomic<uint64_t> account_count{ 0 };
|
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 ();
|
nano::wallet_id random_wallet_id ();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue