Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add option to include votes in confirmation websocket #3016

Merged
merged 14 commits into from
Mar 9, 2021
Merged
Show file tree
Hide file tree
Changes from 13 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion nano/core_test/confirmation_height.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ namespace
{
void add_callback_stats (nano::node & node, std::vector<nano::block_hash> * observer_order = nullptr, std::mutex * mutex = nullptr)
{
node.observers.blocks.add ([& stats = node.stats, observer_order, mutex](nano::election_status const & status_a, nano::account const &, nano::amount const &, bool) {
node.observers.blocks.add ([& stats = node.stats, observer_order, mutex](nano::election_status const & status_a, std::vector<nano::vote_with_weight_info> const &, nano::account const &, nano::amount const &, bool) {
stats.inc (nano::stat::type::http_callback, nano::stat::detail::http_callback, nano::stat::dir::out);
if (mutex)
{
Expand Down
2 changes: 1 addition & 1 deletion nano/core_test/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1476,7 +1476,7 @@ TEST (node, coherent_observer)
{
nano::system system (1);
auto & node1 (*system.nodes[0]);
node1.observers.blocks.add ([&node1](nano::election_status const & status_a, nano::account const &, nano::uint128_t const &, bool) {
node1.observers.blocks.add ([&node1](nano::election_status const & status_a, std::vector<nano::vote_with_weight_info> const &, nano::account const &, nano::uint128_t const &, bool) {
auto transaction (node1.store.tx_begin_read ());
ASSERT_TRUE (node1.store.block_exists (transaction, status_a.winner->hash ()));
});
Expand Down
79 changes: 79 additions & 0 deletions nano/core_test/websocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -302,6 +302,8 @@ TEST (websocket, confirmation_options)
// Make sure tally and time are non-zero.
ASSERT_NE ("0", tally);
ASSERT_NE ("0", time);
auto votes_l (election_info.get_child_optional ("votes"));
ASSERT_FALSE (votes_l.is_initialized ());
}
catch (std::runtime_error const & ex)
{
Expand Down Expand Up @@ -331,7 +333,84 @@ TEST (websocket, confirmation_options)
previous = send->hash ();
}

ASSERT_TIMELY (5s, future3.wait_for (0s) == std::future_status::ready);
}

TEST (websocket, confirmation_options_votes)
{
nano::system system;
nano::node_config config (nano::get_available_port (), system.logging);
config.websocket_config.enabled = true;
config.websocket_config.port = nano::get_available_port ();
auto node1 (system.add_node (config));

std::atomic<bool> ack_ready{ false };
auto task1 = ([&ack_ready, config, &node1]() {
fake_websocket_client client (config.websocket_config.port);
client.send_message (R"json({"action": "subscribe", "topic": "confirmation", "ack": "true", "options": {"confirmation_type": "active_quorum", "include_election_info_with_votes": "true", "include_block": "false"}})json");
client.await_ack ();
ack_ready = true;
EXPECT_EQ (1, node1->websocket_server->subscriber_count (nano::websocket::topic::confirmation));
return client.get_response ();
});
auto future1 = std::async (std::launch::async, task1);

ASSERT_TIMELY (10s, ack_ready);

// Confirm a state block for an in-wallet account
system.wallet (0)->insert_adhoc (nano::dev_genesis_key.prv);
nano::keypair key;
auto balance = nano::genesis_amount;
auto send_amount = node1->config.online_weight_minimum.number () + 1;
nano::block_hash previous (node1->latest (nano::dev_genesis_key.pub));
{
balance -= send_amount;
auto send (std::make_shared<nano::state_block> (nano::dev_genesis_key.pub, previous, nano::dev_genesis_key.pub, balance, key.pub, nano::dev_genesis_key.prv, nano::dev_genesis_key.pub, *system.work.generate (previous)));
node1->process_active (send);
previous = send->hash ();
}

ASSERT_TIMELY (5s, future1.wait_for (0s) == std::future_status::ready);

auto response1 = future1.get ();
ASSERT_TRUE (response1);
boost::property_tree::ptree event;
std::stringstream stream;
stream << response1.get ();
boost::property_tree::read_json (stream, event);
ASSERT_EQ (event.get<std::string> ("topic"), "confirmation");
try
{
boost::property_tree::ptree election_info = event.get_child ("message.election_info");
auto tally (election_info.get<std::string> ("tally"));
auto time (election_info.get<std::string> ("time"));
// Duration and request count may be zero on devnet, so we only check that they're present
ASSERT_EQ (1, election_info.count ("duration"));
ASSERT_EQ (1, election_info.count ("request_count"));
ASSERT_EQ (1, election_info.count ("voters"));
ASSERT_GE (1U, election_info.get<unsigned> ("blocks"));
// Make sure tally and time are non-zero.
ASSERT_NE ("0", tally);
ASSERT_NE ("0", time);
auto votes_l (election_info.get_child_optional ("votes"));
ASSERT_TRUE (votes_l.is_initialized ());
ASSERT_EQ (1, votes_l.get ().size ());
for (auto & vote : votes_l.get ())
{
std::string representative (vote.second.get<std::string> ("representative"));
ASSERT_EQ (nano::dev_genesis_key.pub.to_account (), representative);
std::string timestamp (vote.second.get<std::string> ("timestamp"));
ASSERT_NE ("0", timestamp);
std::string hash (vote.second.get<std::string> ("hash"));
ASSERT_EQ (node1->latest (nano::dev_genesis_key.pub).to_string (), hash);
std::string weight (vote.second.get<std::string> ("weight"));
ASSERT_EQ (node1->balance (nano::dev_genesis_key.pub).convert_to<std::string> (), weight);
}
}
catch (std::runtime_error const & ex)
{
FAIL () << ex.what ();
}
}

// Tests updating options of block confirmations
Expand Down
5 changes: 3 additions & 2 deletions nano/node/active_transactions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ void nano::active_transactions::block_cemented_callback (std::shared_ptr<nano::b
bool is_state_send (false);
nano::account pending_account (0);
node.process_confirmed_data (transaction, block_a, block_a->hash (), account, amount, is_state_send, pending_account);
node.observers.blocks.notify (nano::election_status{ block_a, 0, std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values<std::chrono::milliseconds>::zero (), 0, 1, 0, nano::election_status_type::inactive_confirmation_height }, account, amount, is_state_send);
node.observers.blocks.notify (nano::election_status{ block_a, 0, std::chrono::duration_cast<std::chrono::milliseconds> (std::chrono::system_clock::now ().time_since_epoch ()), std::chrono::duration_values<std::chrono::milliseconds>::zero (), 0, 1, 0, nano::election_status_type::inactive_confirmation_height }, {}, account, amount, is_state_send);
}
else
{
Expand Down Expand Up @@ -218,7 +218,8 @@ void nano::active_transactions::block_cemented_callback (std::shared_ptr<nano::b
election->status.confirmation_request_count = election->confirmation_request_count;
status_l = election->status;
election_lk.unlock ();
node.observers.blocks.notify (status_l, account, amount, is_state_send);
auto votes (election->votes_with_weight ());
node.observers.blocks.notify (status_l, votes, account, amount, is_state_send);
if (amount > 0)
{
node.observers.account_balance.notify (account, false);
Expand Down
20 changes: 19 additions & 1 deletion nano/node/election.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -625,7 +625,25 @@ std::unordered_map<nano::block_hash, std::shared_ptr<nano::block>> nano::electio

std::unordered_map<nano::account, nano::vote_info> nano::election::votes () const
{
debug_assert (node.network_params.network.is_dev_network ());
nano::lock_guard<std::mutex> guard (mutex);
return last_votes;
}

std::vector<nano::vote_with_weight_info> nano::election::votes_with_weight () const
{
std::multimap<nano::uint128_t, nano::vote_with_weight_info, std::greater<nano::uint128_t>> sorted_votes;
std::vector<nano::vote_with_weight_info> result;
auto votes_l (votes ());
for (auto const & vote_l : votes_l)
{
if (vote_l.first != node.network_params.random.not_an_account)
{
auto amount (node.ledger.cache.rep_weights.representation_get (vote_l.first));
nano::vote_with_weight_info vote_info{ vote_l.first, vote_l.second.time, vote_l.second.timestamp, vote_l.second.hash, amount };
sorted_votes.emplace (std::move (amount), vote_info);
}
}
result.reserve (sorted_votes.size ());
std::transform (sorted_votes.begin (), sorted_votes.end (), std::back_inserter (result), [](auto const & entry) { return entry.second; });
return result;
}
10 changes: 10 additions & 0 deletions nano/node/election.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@ class vote_info final
uint64_t timestamp;
nano::block_hash hash;
};
class vote_with_weight_info final
{
public:
nano::account representative;
std::chrono::steady_clock::time_point time;
uint64_t timestamp;
nano::block_hash hash;
nano::uint128_t weight;
};
class election_vote_result final
{
public:
Expand Down Expand Up @@ -117,6 +126,7 @@ class election final : public std::enable_shared_from_this<nano::election>
uint64_t const height;
nano::root const root;
nano::qualified_root const qualified_root;
std::vector<nano::vote_with_weight_info> votes_with_weight () const;

private:
nano::tally_t tally_impl () const;
Expand Down
3 changes: 2 additions & 1 deletion nano/node/ipc/ipc_broker.cpp
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#include <nano/node/election.hpp>
#include <nano/node/ipc/action_handler.hpp>
#include <nano/node/ipc/flatbuffers_handler.hpp>
#include <nano/node/ipc/flatbuffers_util.hpp>
Expand All @@ -21,7 +22,7 @@ std::shared_ptr<flatbuffers::Parser> nano::ipc::subscriber::get_parser (nano::ip

void nano::ipc::broker::start ()
{
node.observers.blocks.add ([this_l = shared_from_this ()](nano::election_status const & status_a, nano::account const & account_a, nano::amount const & amount_a, bool is_state_send_a) {
node.observers.blocks.add ([this_l = shared_from_this ()](nano::election_status const & status_a, std::vector<nano::vote_with_weight_info> const & votes_a, nano::account const & account_a, nano::amount const & amount_a, bool is_state_send_a) {
debug_assert (status_a.type != nano::election_status_type::ongoing);

try
Expand Down
2 changes: 1 addition & 1 deletion nano/node/json_handler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1133,7 +1133,7 @@ void nano::json_handler::block_confirm ()
is_state_send = node.ledger.is_send (transaction, *state);
}
}
node.observers.blocks.notify (status, account, amount, is_state_send);
node.observers.blocks.notify (status, {}, account, amount, is_state_send);
}
response_l.put ("started", "1");
}
Expand Down
8 changes: 4 additions & 4 deletions nano/node/node.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ node_seq (seq)
};
if (!config.callback_address.empty ())
{
observers.blocks.add ([this](nano::election_status const & status_a, nano::account const & account_a, nano::amount const & amount_a, bool is_state_send_a) {
observers.blocks.add ([this](nano::election_status const & status_a, std::vector<nano::vote_with_weight_info> const & votes_a, nano::account const & account_a, nano::amount const & amount_a, bool is_state_send_a) {
auto block_a (status_a.winner);
if ((status_a.type == nano::election_status_type::active_confirmed_quorum || status_a.type == nano::election_status_type::active_confirmation_height) && this->block_arrival.recent (block_a->hash ()))
{
Expand Down Expand Up @@ -213,7 +213,7 @@ node_seq (seq)
}
if (websocket_server)
{
observers.blocks.add ([this](nano::election_status const & status_a, nano::account const & account_a, nano::amount const & amount_a, bool is_state_send_a) {
observers.blocks.add ([this](nano::election_status const & status_a, std::vector<nano::vote_with_weight_info> const & votes_a, nano::account const & account_a, nano::amount const & amount_a, bool is_state_send_a) {
debug_assert (status_a.type != nano::election_status_type::ongoing);

if (this->websocket_server->any_subscriber (nano::websocket::topic::confirmation))
Expand All @@ -240,7 +240,7 @@ node_seq (seq)
}
}

this->websocket_server->broadcast_confirmation (block_a, account_a, amount_a, subtype, status_a);
this->websocket_server->broadcast_confirmation (block_a, account_a, amount_a, subtype, status_a, votes_a);
}
});

Expand Down Expand Up @@ -270,7 +270,7 @@ node_seq (seq)
});
}
// Add block confirmation type stats regardless of http-callback and websocket subscriptions
observers.blocks.add ([this](nano::election_status const & status_a, nano::account const & account_a, nano::amount const & amount_a, bool is_state_send_a) {
observers.blocks.add ([this](nano::election_status const & status_a, std::vector<nano::vote_with_weight_info> const & votes_a, nano::account const & account_a, nano::amount const & amount_a, bool is_state_send_a) {
debug_assert (status_a.type != nano::election_status_type::ongoing);
switch (status_a.type)
{
Expand Down
2 changes: 1 addition & 1 deletion nano/node/node_observers.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class telemetry;
class node_observers final
{
public:
using blocks_t = nano::observer_set<nano::election_status const &, nano::account const &, nano::uint128_t const &, bool>;
using blocks_t = nano::observer_set<nano::election_status const &, std::vector<nano::vote_with_weight_info> const &, nano::account const &, nano::uint128_t const &, bool>;
blocks_t blocks;
nano::observer_set<bool> wallet;
nano::observer_set<std::shared_ptr<nano::vote>, std::shared_ptr<nano::transport::channel>, nano::vote_code> vote;
Expand Down
2 changes: 1 addition & 1 deletion nano/node/wallet.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1323,7 +1323,7 @@ nano::work_watcher::work_watcher (nano::node & node_a) :
node (node_a),
stopped (false)
{
node.observers.blocks.add ([this](nano::election_status const & status_a, nano::account const & account_a, nano::amount const & amount_a, bool is_state_send_a) {
node.observers.blocks.add ([this](nano::election_status const & status_a, std::vector<nano::vote_with_weight_info> const & votes_a, nano::account const & account_a, nano::amount const & amount_a, bool is_state_send_a) {
this->remove (*status_a.winner);
});
}
Expand Down
26 changes: 20 additions & 6 deletions nano/node/websocket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
#include <nano/boost/asio/dispatch.hpp>
#include <nano/boost/asio/strand.hpp>
#include <nano/lib/work.hpp>
#include <nano/node/election.hpp>
#include <nano/node/transport/transport.hpp>
#include <nano/node/wallet.hpp>
#include <nano/node/websocket.hpp>
Expand All @@ -25,6 +24,7 @@ logger (logger_a)
// Non-account filtering options
include_block = options_a.get<bool> ("include_block", true);
include_election_info = options_a.get<bool> ("include_election_info", false);
include_election_info_with_votes = options_a.get<bool> ("include_election_info_with_votes", false);

confirmation_types = 0;
auto type_l (options_a.get<std::string> ("confirmation_type", "all"));
Expand Down Expand Up @@ -612,7 +612,7 @@ void nano::websocket::listener::on_accept (boost::system::error_code ec)
}
}

void nano::websocket::listener::broadcast_confirmation (std::shared_ptr<nano::block> const & block_a, nano::account const & account_a, nano::amount const & amount_a, std::string const & subtype, nano::election_status const & election_status_a)
void nano::websocket::listener::broadcast_confirmation (std::shared_ptr<nano::block> const & block_a, nano::account const & account_a, nano::amount const & amount_a, std::string const & subtype, nano::election_status const & election_status_a, std::vector<nano::vote_with_weight_info> const & election_votes_a)
{
nano::websocket::message_builder builder;

Expand All @@ -637,11 +637,11 @@ void nano::websocket::listener::broadcast_confirmation (std::shared_ptr<nano::bl

if (include_block && !msg_with_block)
{
msg_with_block = builder.block_confirmed (block_a, account_a, amount_a, subtype, include_block, election_status_a, *conf_options);
msg_with_block = builder.block_confirmed (block_a, account_a, amount_a, subtype, include_block, election_status_a, election_votes_a, *conf_options);
}
else if (!include_block && !msg_without_block)
{
msg_without_block = builder.block_confirmed (block_a, account_a, amount_a, subtype, include_block, election_status_a, *conf_options);
msg_without_block = builder.block_confirmed (block_a, account_a, amount_a, subtype, include_block, election_status_a, election_votes_a, *conf_options);
}
else
{
Expand Down Expand Up @@ -691,7 +691,7 @@ nano::websocket::message nano::websocket::message_builder::stopped_election (nan
return message_l;
}

nano::websocket::message nano::websocket::message_builder::block_confirmed (std::shared_ptr<nano::block> const & block_a, nano::account const & account_a, nano::amount const & amount_a, std::string subtype, bool include_block_a, nano::election_status const & election_status_a, nano::websocket::confirmation_options const & options_a)
nano::websocket::message nano::websocket::message_builder::block_confirmed (std::shared_ptr<nano::block> const & block_a, nano::account const & account_a, nano::amount const & amount_a, std::string subtype, bool include_block_a, nano::election_status const & election_status_a, std::vector<nano::vote_with_weight_info> const & election_votes_a, nano::websocket::confirmation_options const & options_a)
{
nano::websocket::message message_l (nano::websocket::topic::confirmation);
set_common_fields (message_l);
Expand Down Expand Up @@ -719,7 +719,7 @@ nano::websocket::message nano::websocket::message_builder::block_confirmed (std:
};
message_node_l.add ("confirmation_type", confirmation_type);

if (options_a.get_include_election_info ())
if (options_a.get_include_election_info () || options_a.get_include_election_info_with_votes ())
{
boost::property_tree::ptree election_node_l;
election_node_l.add ("duration", election_status_a.election_duration.count ());
Expand All @@ -728,6 +728,20 @@ nano::websocket::message nano::websocket::message_builder::block_confirmed (std:
election_node_l.add ("blocks", std::to_string (election_status_a.block_count));
election_node_l.add ("voters", std::to_string (election_status_a.voter_count));
election_node_l.add ("request_count", std::to_string (election_status_a.confirmation_request_count));
if (options_a.get_include_election_info_with_votes ())
{
boost::property_tree::ptree election_votes_l;
for (auto const & vote_l : election_votes_a)
{
boost::property_tree::ptree entry;
entry.put ("representative", vote_l.representative.to_account ());
entry.put ("timestamp", vote_l.timestamp);
entry.put ("hash", vote_l.hash.to_string ());
entry.put ("weight", vote_l.weight.convert_to<std::string> ());
election_votes_l.push_back (std::make_pair ("", entry));
}
election_node_l.add_child ("votes", election_votes_l);
}
message_node_l.add_child ("election_info", election_node_l);
}

Expand Down
Loading