Move vote class code to a separate file (#4338)
* Move vote class code to a separate file * Cleanup
This commit is contained in:
parent
58a8a6bd19
commit
083f243705
5 changed files with 268 additions and 243 deletions
|
@ -47,6 +47,8 @@ add_library(
|
|||
network_filter.cpp
|
||||
utility.hpp
|
||||
utility.cpp
|
||||
vote.hpp
|
||||
vote.cpp
|
||||
working.hpp)
|
||||
|
||||
target_link_libraries(secure nano_lib ed25519 crypto_lib Boost::system)
|
||||
|
|
|
@ -430,191 +430,6 @@ nano::block_info::block_info (nano::account const & account_a, nano::amount cons
|
|||
{
|
||||
}
|
||||
|
||||
bool nano::vote::operator== (nano::vote const & other_a) const
|
||||
{
|
||||
return timestamp_m == other_a.timestamp_m && hashes == other_a.hashes && account == other_a.account && signature == other_a.signature;
|
||||
}
|
||||
|
||||
bool nano::vote::operator!= (nano::vote const & other_a) const
|
||||
{
|
||||
return !(*this == other_a);
|
||||
}
|
||||
|
||||
void nano::vote::serialize_json (boost::property_tree::ptree & tree) const
|
||||
{
|
||||
tree.put ("account", account.to_account ());
|
||||
tree.put ("signature", signature.number ());
|
||||
tree.put ("sequence", std::to_string (timestamp ()));
|
||||
tree.put ("timestamp", std::to_string (timestamp ()));
|
||||
tree.put ("duration", std::to_string (duration_bits ()));
|
||||
boost::property_tree::ptree blocks_tree;
|
||||
for (auto const & hash : hashes)
|
||||
{
|
||||
boost::property_tree::ptree entry;
|
||||
entry.put ("", hash.to_string ());
|
||||
blocks_tree.push_back (std::make_pair ("", entry));
|
||||
}
|
||||
tree.add_child ("blocks", blocks_tree);
|
||||
}
|
||||
|
||||
std::string nano::vote::to_json () const
|
||||
{
|
||||
std::stringstream stream;
|
||||
boost::property_tree::ptree tree;
|
||||
serialize_json (tree);
|
||||
boost::property_tree::write_json (stream, tree);
|
||||
return stream.str ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp of the vote (with the duration bits masked, set to zero)
|
||||
* If it is a final vote, all the bits including duration bits are returned as they are, all FF
|
||||
*/
|
||||
uint64_t nano::vote::timestamp () const
|
||||
{
|
||||
return (timestamp_m == std::numeric_limits<uint64_t>::max ())
|
||||
? timestamp_m // final vote
|
||||
: (timestamp_m & timestamp_mask);
|
||||
}
|
||||
|
||||
uint8_t nano::vote::duration_bits () const
|
||||
{
|
||||
// Duration field is specified in the 4 low-order bits of the timestamp.
|
||||
// This makes the timestamp have a minimum granularity of 16ms
|
||||
// The duration is specified as 2^(duration + 4) giving it a range of 16-524,288ms in power of two increments
|
||||
auto result = timestamp_m & ~timestamp_mask;
|
||||
debug_assert (result < 16);
|
||||
return static_cast<uint8_t> (result);
|
||||
}
|
||||
|
||||
std::chrono::milliseconds nano::vote::duration () const
|
||||
{
|
||||
return std::chrono::milliseconds{ 1u << (duration_bits () + 4) };
|
||||
}
|
||||
|
||||
nano::vote::vote (nano::vote const & other_a) :
|
||||
timestamp_m{ other_a.timestamp_m },
|
||||
hashes{ other_a.hashes },
|
||||
account (other_a.account),
|
||||
signature (other_a.signature)
|
||||
{
|
||||
}
|
||||
|
||||
nano::vote::vote (bool & error_a, nano::stream & stream_a)
|
||||
{
|
||||
error_a = deserialize (stream_a);
|
||||
}
|
||||
|
||||
nano::vote::vote (nano::account const & account_a, nano::raw_key const & prv_a, uint64_t timestamp_a, uint8_t duration, std::vector<nano::block_hash> const & hashes) :
|
||||
hashes{ hashes },
|
||||
timestamp_m{ packed_timestamp (timestamp_a, duration) },
|
||||
account (account_a)
|
||||
{
|
||||
signature = nano::sign_message (prv_a, account_a, hash ());
|
||||
}
|
||||
|
||||
std::string nano::vote::hashes_string () const
|
||||
{
|
||||
std::string result;
|
||||
for (auto const & hash : hashes)
|
||||
{
|
||||
result += hash.to_string ();
|
||||
result += ", ";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::string const nano::vote::hash_prefix = "vote ";
|
||||
|
||||
nano::block_hash nano::vote::hash () const
|
||||
{
|
||||
nano::block_hash result;
|
||||
blake2b_state hash;
|
||||
blake2b_init (&hash, sizeof (result.bytes));
|
||||
blake2b_update (&hash, hash_prefix.data (), hash_prefix.size ());
|
||||
for (auto const & block_hash : hashes)
|
||||
{
|
||||
blake2b_update (&hash, block_hash.bytes.data (), sizeof (block_hash.bytes));
|
||||
}
|
||||
union
|
||||
{
|
||||
uint64_t qword;
|
||||
std::array<uint8_t, 8> bytes;
|
||||
};
|
||||
qword = timestamp_m;
|
||||
blake2b_update (&hash, bytes.data (), sizeof (bytes));
|
||||
blake2b_final (&hash, result.bytes.data (), sizeof (result.bytes));
|
||||
return result;
|
||||
}
|
||||
|
||||
nano::block_hash nano::vote::full_hash () const
|
||||
{
|
||||
nano::block_hash result;
|
||||
blake2b_state state;
|
||||
blake2b_init (&state, sizeof (result.bytes));
|
||||
blake2b_update (&state, hash ().bytes.data (), sizeof (hash ().bytes));
|
||||
blake2b_update (&state, account.bytes.data (), sizeof (account.bytes.data ()));
|
||||
blake2b_update (&state, signature.bytes.data (), sizeof (signature.bytes.data ()));
|
||||
blake2b_final (&state, result.bytes.data (), sizeof (result.bytes));
|
||||
return result;
|
||||
}
|
||||
|
||||
void nano::vote::serialize (nano::stream & stream_a) const
|
||||
{
|
||||
write (stream_a, account);
|
||||
write (stream_a, signature);
|
||||
write (stream_a, boost::endian::native_to_little (timestamp_m));
|
||||
for (auto const & hash : hashes)
|
||||
{
|
||||
write (stream_a, hash);
|
||||
}
|
||||
}
|
||||
|
||||
bool nano::vote::deserialize (nano::stream & stream_a)
|
||||
{
|
||||
auto error = false;
|
||||
try
|
||||
{
|
||||
nano::read (stream_a, account.bytes);
|
||||
nano::read (stream_a, signature.bytes);
|
||||
nano::read (stream_a, timestamp_m);
|
||||
|
||||
while (stream_a.in_avail () > 0)
|
||||
{
|
||||
nano::block_hash block_hash;
|
||||
nano::read (stream_a, block_hash);
|
||||
hashes.push_back (block_hash);
|
||||
}
|
||||
}
|
||||
catch (std::runtime_error const &)
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
bool nano::vote::validate () const
|
||||
{
|
||||
return nano::validate_message (account, hash (), signature);
|
||||
}
|
||||
|
||||
uint64_t nano::vote::packed_timestamp (uint64_t timestamp, uint8_t duration) const
|
||||
{
|
||||
debug_assert (duration <= duration_max && "Invalid duration");
|
||||
debug_assert ((!(timestamp == timestamp_max) || (duration == duration_max)) && "Invalid final vote");
|
||||
return (timestamp & timestamp_mask) | duration;
|
||||
}
|
||||
|
||||
bool nano::vote::is_final_timestamp (uint64_t timestamp)
|
||||
{
|
||||
return timestamp == std::numeric_limits<uint64_t>::max ();
|
||||
}
|
||||
|
||||
nano::block_hash nano::iterate_vote_blocks_as_hash::operator() (nano::block_hash const & item) const
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
nano::vote_uniquer::vote_uniquer (nano::block_uniquer & uniquer_a) :
|
||||
uniquer (uniquer_a)
|
||||
{
|
||||
|
|
|
@ -10,6 +10,7 @@
|
|||
#include <nano/lib/stats.hpp>
|
||||
#include <nano/lib/timer.hpp>
|
||||
#include <nano/lib/utility.hpp>
|
||||
#include <nano/secure/vote.hpp>
|
||||
|
||||
#include <boost/iterator/transform_iterator.hpp>
|
||||
#include <boost/optional/optional.hpp>
|
||||
|
@ -243,64 +244,6 @@ namespace confirmation_height
|
|||
uint64_t const unbounded_cutoff{ 16384 };
|
||||
}
|
||||
|
||||
using vote_blocks_vec_iter = std::vector<nano::block_hash>::const_iterator;
|
||||
class iterate_vote_blocks_as_hash final
|
||||
{
|
||||
public:
|
||||
iterate_vote_blocks_as_hash () = default;
|
||||
nano::block_hash operator() (nano::block_hash const & item) const;
|
||||
};
|
||||
class vote final
|
||||
{
|
||||
public:
|
||||
vote () = default;
|
||||
vote (nano::vote const &);
|
||||
vote (bool &, nano::stream &);
|
||||
vote (nano::account const &, nano::raw_key const &, nano::millis_t timestamp, uint8_t duration, std::vector<nano::block_hash> const &);
|
||||
std::string hashes_string () const;
|
||||
nano::block_hash hash () const;
|
||||
nano::block_hash full_hash () const;
|
||||
bool operator== (nano::vote const &) const;
|
||||
bool operator!= (nano::vote const &) const;
|
||||
void serialize (nano::stream &) const;
|
||||
void serialize_json (boost::property_tree::ptree & tree) const;
|
||||
/**
|
||||
* Deserializes a vote from the bytes in `stream'
|
||||
* Returns true if there was an error
|
||||
*/
|
||||
bool deserialize (nano::stream &);
|
||||
bool validate () const;
|
||||
boost::transform_iterator<nano::iterate_vote_blocks_as_hash, nano::vote_blocks_vec_iter> begin () const;
|
||||
boost::transform_iterator<nano::iterate_vote_blocks_as_hash, nano::vote_blocks_vec_iter> end () const;
|
||||
std::string to_json () const;
|
||||
uint64_t timestamp () const;
|
||||
uint8_t duration_bits () const;
|
||||
std::chrono::milliseconds duration () const;
|
||||
|
||||
static uint64_t constexpr timestamp_mask = { 0xffff'ffff'ffff'fff0ULL };
|
||||
static nano::seconds_t constexpr timestamp_max = { 0xffff'ffff'ffff'fff0ULL };
|
||||
static uint64_t constexpr timestamp_min = { 0x0000'0000'0000'0010ULL };
|
||||
static uint8_t constexpr duration_max = { 0x0fu };
|
||||
|
||||
/* Check if timestamp represents a final vote */
|
||||
static bool is_final_timestamp (uint64_t timestamp);
|
||||
|
||||
private:
|
||||
// Vote timestamp
|
||||
uint64_t timestamp_m;
|
||||
|
||||
public:
|
||||
// The hashes for which this vote directly covers
|
||||
std::vector<nano::block_hash> hashes;
|
||||
// Account that's voting
|
||||
nano::account account;
|
||||
// Signature of timestamp + block hashes
|
||||
nano::signature signature;
|
||||
static std::string const hash_prefix;
|
||||
|
||||
private:
|
||||
uint64_t packed_timestamp (uint64_t timestamp, uint8_t duration) const;
|
||||
};
|
||||
/**
|
||||
* This class serves to find and return unique variants of a vote in order to minimize memory usage
|
||||
*/
|
||||
|
|
185
nano/secure/vote.cpp
Normal file
185
nano/secure/vote.cpp
Normal file
|
@ -0,0 +1,185 @@
|
|||
#include <nano/secure/common.hpp>
|
||||
#include <nano/secure/vote.hpp>
|
||||
|
||||
#include <boost/property_tree/json_parser.hpp>
|
||||
|
||||
nano::vote::vote (bool & error_a, nano::stream & stream_a)
|
||||
{
|
||||
error_a = deserialize (stream_a);
|
||||
}
|
||||
|
||||
nano::vote::vote (nano::account const & account_a, nano::raw_key const & prv_a, uint64_t timestamp_a, uint8_t duration, std::vector<nano::block_hash> const & hashes) :
|
||||
hashes{ hashes },
|
||||
timestamp_m{ packed_timestamp (timestamp_a, duration) },
|
||||
account{ account_a }
|
||||
{
|
||||
signature = nano::sign_message (prv_a, account_a, hash ());
|
||||
}
|
||||
|
||||
void nano::vote::serialize (nano::stream & stream_a) const
|
||||
{
|
||||
write (stream_a, account);
|
||||
write (stream_a, signature);
|
||||
write (stream_a, boost::endian::native_to_little (timestamp_m));
|
||||
for (auto const & hash : hashes)
|
||||
{
|
||||
write (stream_a, hash);
|
||||
}
|
||||
}
|
||||
|
||||
bool nano::vote::deserialize (nano::stream & stream_a)
|
||||
{
|
||||
auto error = false;
|
||||
try
|
||||
{
|
||||
nano::read (stream_a, account.bytes);
|
||||
nano::read (stream_a, signature.bytes);
|
||||
nano::read (stream_a, timestamp_m);
|
||||
|
||||
while (stream_a.in_avail () > 0)
|
||||
{
|
||||
nano::block_hash block_hash;
|
||||
nano::read (stream_a, block_hash);
|
||||
hashes.push_back (block_hash);
|
||||
}
|
||||
}
|
||||
catch (std::runtime_error const &)
|
||||
{
|
||||
error = true;
|
||||
}
|
||||
return error;
|
||||
}
|
||||
|
||||
std::string const nano::vote::hash_prefix = "vote ";
|
||||
|
||||
nano::block_hash nano::vote::hash () const
|
||||
{
|
||||
nano::block_hash result;
|
||||
blake2b_state hash;
|
||||
blake2b_init (&hash, sizeof (result.bytes));
|
||||
blake2b_update (&hash, hash_prefix.data (), hash_prefix.size ());
|
||||
for (auto const & block_hash : hashes)
|
||||
{
|
||||
blake2b_update (&hash, block_hash.bytes.data (), sizeof (block_hash.bytes));
|
||||
}
|
||||
union
|
||||
{
|
||||
uint64_t qword;
|
||||
std::array<uint8_t, 8> bytes;
|
||||
};
|
||||
qword = timestamp_m;
|
||||
blake2b_update (&hash, bytes.data (), sizeof (bytes));
|
||||
blake2b_final (&hash, result.bytes.data (), sizeof (result.bytes));
|
||||
return result;
|
||||
}
|
||||
|
||||
nano::block_hash nano::vote::full_hash () const
|
||||
{
|
||||
nano::block_hash result;
|
||||
blake2b_state state;
|
||||
blake2b_init (&state, sizeof (result.bytes));
|
||||
blake2b_update (&state, hash ().bytes.data (), sizeof (hash ().bytes));
|
||||
blake2b_update (&state, account.bytes.data (), sizeof (account.bytes.data ()));
|
||||
blake2b_update (&state, signature.bytes.data (), sizeof (signature.bytes.data ()));
|
||||
blake2b_final (&state, result.bytes.data (), sizeof (result.bytes));
|
||||
return result;
|
||||
}
|
||||
|
||||
bool nano::vote::validate () const
|
||||
{
|
||||
return nano::validate_message (account, hash (), signature);
|
||||
}
|
||||
|
||||
bool nano::vote::operator== (nano::vote const & other_a) const
|
||||
{
|
||||
return timestamp_m == other_a.timestamp_m && hashes == other_a.hashes && account == other_a.account && signature == other_a.signature;
|
||||
}
|
||||
|
||||
bool nano::vote::operator!= (nano::vote const & other_a) const
|
||||
{
|
||||
return !(*this == other_a);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the timestamp of the vote (with the duration bits masked, set to zero)
|
||||
* If it is a final vote, all the bits including duration bits are returned as they are, all FF
|
||||
*/
|
||||
uint64_t nano::vote::timestamp () const
|
||||
{
|
||||
return (timestamp_m == std::numeric_limits<uint64_t>::max ())
|
||||
? timestamp_m // final vote
|
||||
: (timestamp_m & timestamp_mask);
|
||||
}
|
||||
|
||||
uint8_t nano::vote::duration_bits () const
|
||||
{
|
||||
// Duration field is specified in the 4 low-order bits of the timestamp.
|
||||
// This makes the timestamp have a minimum granularity of 16ms
|
||||
// The duration is specified as 2^(duration + 4) giving it a range of 16-524,288ms in power of two increments
|
||||
auto result = timestamp_m & ~timestamp_mask;
|
||||
debug_assert (result < 16);
|
||||
return static_cast<uint8_t> (result);
|
||||
}
|
||||
|
||||
std::chrono::milliseconds nano::vote::duration () const
|
||||
{
|
||||
return std::chrono::milliseconds{ 1u << (duration_bits () + 4) };
|
||||
}
|
||||
|
||||
void nano::vote::serialize_json (boost::property_tree::ptree & tree) const
|
||||
{
|
||||
tree.put ("account", account.to_account ());
|
||||
tree.put ("signature", signature.number ());
|
||||
tree.put ("sequence", std::to_string (timestamp ()));
|
||||
tree.put ("timestamp", std::to_string (timestamp ()));
|
||||
tree.put ("duration", std::to_string (duration_bits ()));
|
||||
boost::property_tree::ptree blocks_tree;
|
||||
for (auto const & hash : hashes)
|
||||
{
|
||||
boost::property_tree::ptree entry;
|
||||
entry.put ("", hash.to_string ());
|
||||
blocks_tree.push_back (std::make_pair ("", entry));
|
||||
}
|
||||
tree.add_child ("blocks", blocks_tree);
|
||||
}
|
||||
|
||||
std::string nano::vote::to_json () const
|
||||
{
|
||||
std::stringstream stream;
|
||||
boost::property_tree::ptree tree;
|
||||
serialize_json (tree);
|
||||
boost::property_tree::write_json (stream, tree);
|
||||
return stream.str ();
|
||||
}
|
||||
|
||||
std::string nano::vote::hashes_string () const
|
||||
{
|
||||
std::string result;
|
||||
for (auto const & hash : hashes)
|
||||
{
|
||||
result += hash.to_string ();
|
||||
result += ", ";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint64_t nano::vote::packed_timestamp (uint64_t timestamp, uint8_t duration)
|
||||
{
|
||||
debug_assert (duration <= duration_max && "Invalid duration");
|
||||
debug_assert ((!(timestamp == timestamp_max) || (duration == duration_max)) && "Invalid final vote");
|
||||
return (timestamp & timestamp_mask) | duration;
|
||||
}
|
||||
|
||||
bool nano::vote::is_final_timestamp (uint64_t timestamp)
|
||||
{
|
||||
return timestamp == std::numeric_limits<uint64_t>::max ();
|
||||
}
|
||||
|
||||
/*
|
||||
* iterate_vote_blocks_as_hash
|
||||
*/
|
||||
|
||||
nano::block_hash nano::iterate_vote_blocks_as_hash::operator() (nano::block_hash const & item) const
|
||||
{
|
||||
return item;
|
||||
}
|
80
nano/secure/vote.hpp
Normal file
80
nano/secure/vote.hpp
Normal file
|
@ -0,0 +1,80 @@
|
|||
#pragma once
|
||||
|
||||
#include <nano/lib/numbers.hpp>
|
||||
#include <nano/lib/stream.hpp>
|
||||
#include <nano/lib/timer.hpp>
|
||||
|
||||
#include <boost/iterator/transform_iterator.hpp>
|
||||
#include <boost/property_tree/ptree_fwd.hpp>
|
||||
|
||||
#include <vector>
|
||||
|
||||
namespace nano
|
||||
{
|
||||
using vote_blocks_vec_iter = std::vector<nano::block_hash>::const_iterator;
|
||||
class iterate_vote_blocks_as_hash final
|
||||
{
|
||||
public:
|
||||
iterate_vote_blocks_as_hash () = default;
|
||||
nano::block_hash operator() (nano::block_hash const & item) const;
|
||||
};
|
||||
|
||||
class vote final
|
||||
{
|
||||
public:
|
||||
vote () = default;
|
||||
vote (nano::vote const &) = default;
|
||||
vote (bool & error, nano::stream &);
|
||||
vote (nano::account const &, nano::raw_key const &, nano::millis_t timestamp, uint8_t duration, std::vector<nano::block_hash> const & hashes);
|
||||
|
||||
void serialize (nano::stream &) const;
|
||||
/**
|
||||
* Deserializes a vote from the bytes in `stream'
|
||||
* @returns true if there was an error
|
||||
*/
|
||||
bool deserialize (nano::stream &);
|
||||
|
||||
nano::block_hash hash () const;
|
||||
nano::block_hash full_hash () const;
|
||||
bool validate () const;
|
||||
|
||||
bool operator== (nano::vote const &) const;
|
||||
bool operator!= (nano::vote const &) const;
|
||||
|
||||
boost::transform_iterator<nano::iterate_vote_blocks_as_hash, nano::vote_blocks_vec_iter> begin () const;
|
||||
boost::transform_iterator<nano::iterate_vote_blocks_as_hash, nano::vote_blocks_vec_iter> end () const;
|
||||
|
||||
void serialize_json (boost::property_tree::ptree & tree) const;
|
||||
std::string to_json () const;
|
||||
std::string hashes_string () const;
|
||||
|
||||
uint64_t timestamp () const;
|
||||
uint8_t duration_bits () const;
|
||||
std::chrono::milliseconds duration () const;
|
||||
|
||||
static uint64_t constexpr timestamp_mask = { 0xffff'ffff'ffff'fff0ULL };
|
||||
static nano::seconds_t constexpr timestamp_max = { 0xffff'ffff'ffff'fff0ULL };
|
||||
static uint64_t constexpr timestamp_min = { 0x0000'0000'0000'0010ULL };
|
||||
static uint8_t constexpr duration_max = { 0x0fu };
|
||||
|
||||
/* Check if timestamp represents a final vote */
|
||||
static bool is_final_timestamp (uint64_t timestamp);
|
||||
|
||||
private:
|
||||
static std::string const hash_prefix;
|
||||
|
||||
static uint64_t packed_timestamp (uint64_t timestamp, uint8_t duration);
|
||||
|
||||
public: // Payload
|
||||
// The hashes for which this vote directly covers
|
||||
std::vector<nano::block_hash> hashes;
|
||||
// Account that's voting
|
||||
nano::account account{ 0 };
|
||||
// Signature of timestamp + block hashes
|
||||
nano::signature signature{ 0 };
|
||||
|
||||
private: // Payload
|
||||
// Vote timestamp
|
||||
uint64_t timestamp_m{ 0 };
|
||||
};
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue