dncurrency/nano/lib/blocks.cpp
2024-11-04 11:08:55 +01:00

1939 lines
46 KiB
C++

#include <nano/crypto_lib/random_pool.hpp>
#include <nano/lib/block_type.hpp>
#include <nano/lib/block_uniquer.hpp>
#include <nano/lib/blocks.hpp>
#include <nano/lib/enum_util.hpp>
#include <nano/lib/memory.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/stream.hpp>
#include <nano/lib/threading.hpp>
#include <nano/lib/work_version.hpp>
#include <nano/secure/common.hpp>
#include <boost/endian/conversion.hpp>
#include <boost/property_tree/json_parser.hpp>
#include <bitset>
#include <cryptopp/words.h>
size_t constexpr nano::send_block::size;
size_t constexpr nano::receive_block::size;
size_t constexpr nano::open_block::size;
size_t constexpr nano::change_block::size;
size_t constexpr nano::state_block::size;
/** Compare blocks, first by type, then content. This is an optimization over dynamic_cast, which is very slow on some platforms. */
namespace
{
template <typename T>
bool blocks_equal (T const & first, nano::block const & second)
{
static_assert (std::is_base_of<nano::block, T>::value, "Input parameter is not a block type");
return (first.type () == second.type ()) && (static_cast<T const &> (second)) == first;
}
template <typename block>
std::shared_ptr<block> deserialize_block (nano::stream & stream_a)
{
auto error (false);
auto result = nano::make_shared<block> (error, stream_a);
if (error)
{
result = nullptr;
}
return result;
}
}
void nano::block_memory_pool_purge ()
{
nano::purge_shared_ptr_singleton_pool_memory<nano::open_block> ();
nano::purge_shared_ptr_singleton_pool_memory<nano::state_block> ();
nano::purge_shared_ptr_singleton_pool_memory<nano::send_block> ();
nano::purge_shared_ptr_singleton_pool_memory<nano::change_block> ();
}
/*
* block
*/
std::string nano::block::to_json () const
{
std::string result;
serialize_json (result);
return result;
}
size_t nano::block::size (nano::block_type type_a)
{
size_t result (0);
switch (type_a)
{
case nano::block_type::invalid:
case nano::block_type::not_a_block:
debug_assert (false);
break;
case nano::block_type::send:
result = nano::send_block::size;
break;
case nano::block_type::receive:
result = nano::receive_block::size;
break;
case nano::block_type::change:
result = nano::change_block::size;
break;
case nano::block_type::open:
result = nano::open_block::size;
break;
case nano::block_type::state:
result = nano::state_block::size;
break;
}
return result;
}
nano::work_version nano::block::work_version () const
{
return nano::work_version::work_1;
}
nano::block_hash nano::block::generate_hash () const
{
nano::block_hash result;
blake2b_state hash_l;
auto status (blake2b_init (&hash_l, sizeof (result.bytes)));
debug_assert (status == 0);
generate_hash (hash_l);
status = blake2b_final (&hash_l, result.bytes.data (), sizeof (result.bytes));
debug_assert (status == 0);
return result;
}
void nano::block::refresh ()
{
if (!cached_hash.is_zero ())
{
cached_hash = generate_hash ();
}
}
bool nano::block::is_send () const noexcept
{
release_assert (has_sideband ());
switch (type ())
{
case nano::block_type::send:
return true;
case nano::block_type::state:
return sideband ().details.is_send;
default:
return false;
}
}
bool nano::block::is_receive () const noexcept
{
release_assert (has_sideband ());
switch (type ())
{
case nano::block_type::receive:
case nano::block_type::open:
return true;
case nano::block_type::state:
return sideband ().details.is_receive;
default:
return false;
}
}
bool nano::block::is_change () const noexcept
{
release_assert (has_sideband ());
switch (type ())
{
case nano::block_type::change:
return true;
case nano::block_type::state:
if (link_field ().value ().is_zero ())
{
return true;
}
return false;
default:
return false;
}
}
bool nano::block::is_epoch () const noexcept
{
release_assert (has_sideband ());
switch (type ())
{
case nano::block_type::state:
return sideband ().details.is_epoch;
default:
return false;
}
}
nano::block_hash const & nano::block::hash () const
{
if (!cached_hash.is_zero ())
{
// Once a block is created, it should not be modified (unless using refresh ())
// This would invalidate the cache; check it hasn't changed.
debug_assert (cached_hash == generate_hash ());
}
else
{
cached_hash = generate_hash ();
}
return cached_hash;
}
nano::block_hash nano::block::full_hash () const
{
nano::block_hash result;
blake2b_state state;
blake2b_init (&state, sizeof (result.bytes));
blake2b_update (&state, hash ().bytes.data (), sizeof (hash ()));
auto signature (block_signature ());
blake2b_update (&state, signature.bytes.data (), sizeof (signature));
auto work (block_work ());
blake2b_update (&state, &work, sizeof (work));
blake2b_final (&state, result.bytes.data (), sizeof (result.bytes));
return result;
}
nano::block_sideband const & nano::block::sideband () const
{
release_assert (sideband_m.is_initialized ());
return *sideband_m;
}
void nano::block::sideband_set (nano::block_sideband const & sideband_a)
{
sideband_m = sideband_a;
}
bool nano::block::has_sideband () const
{
return sideband_m.is_initialized ();
}
std::optional<nano::account> nano::block::representative_field () const
{
return std::nullopt;
}
std::optional<nano::block_hash> nano::block::source_field () const
{
return std::nullopt;
}
std::optional<nano::account> nano::block::destination_field () const
{
return std::nullopt;
}
std::optional<nano::link> nano::block::link_field () const
{
return std::nullopt;
}
nano::account nano::block::account () const noexcept
{
release_assert (has_sideband ());
switch (type ())
{
case block_type::open:
case block_type::state:
return account_field ().value ();
case block_type::change:
case block_type::send:
case block_type::receive:
return sideband ().account;
default:
release_assert (false);
}
}
nano::amount nano::block::balance () const noexcept
{
release_assert (has_sideband ());
switch (type ())
{
case nano::block_type::open:
case nano::block_type::receive:
case nano::block_type::change:
return sideband ().balance;
case nano::block_type::send:
case nano::block_type::state:
return balance_field ().value ();
default:
release_assert (false);
}
}
nano::account nano::block::destination () const noexcept
{
release_assert (has_sideband ());
switch (type ())
{
case nano::block_type::send:
return destination_field ().value ();
case nano::block_type::state:
release_assert (sideband ().details.is_send);
return link_field ().value ().as_account ();
default:
release_assert (false);
}
}
nano::block_hash nano::block::source () const noexcept
{
release_assert (has_sideband ());
switch (type ())
{
case nano::block_type::open:
case nano::block_type::receive:
return source_field ().value ();
case nano::block_type::state:
release_assert (sideband ().details.is_receive);
return link_field ().value ().as_block_hash ();
default:
release_assert (false);
}
}
// TODO - Remove comments below and fixup usages to not need to check .is_zero ()
// std::optional<nano::block_hash> nano::block::previous () const
nano::block_hash nano::block::previous () const noexcept
{
std::optional<nano::block_hash> result = previous_field ();
/*
if (result && result.value ().is_zero ())
{
return std::nullopt;
}
return result;*/
return result.value_or (0);
}
std::optional<nano::account> nano::block::account_field () const
{
return std::nullopt;
}
nano::qualified_root nano::block::qualified_root () const
{
return { root (), previous () };
}
std::optional<nano::amount> nano::block::balance_field () const
{
return std::nullopt;
}
void nano::block::operator() (nano::object_stream & obs) const
{
obs.write ("type", type ());
obs.write ("hash", hash ());
if (has_sideband ())
{
obs.write ("sideband", sideband ());
}
}
/*
* send_block
*/
void nano::send_block::visit (nano::block_visitor & visitor_a) const
{
visitor_a.send_block (*this);
}
void nano::send_block::visit (nano::mutable_block_visitor & visitor_a)
{
visitor_a.send_block (*this);
}
void nano::send_block::generate_hash (blake2b_state & hash_a) const
{
hashables.hash (hash_a);
}
uint64_t nano::send_block::block_work () const
{
return work;
}
void nano::send_block::block_work_set (uint64_t work_a)
{
work = work_a;
}
nano::send_hashables::send_hashables (nano::block_hash const & previous_a, nano::account const & destination_a, nano::amount const & balance_a) :
previous (previous_a),
destination (destination_a),
balance (balance_a)
{
}
nano::send_hashables::send_hashables (bool & error_a, nano::stream & stream_a)
{
try
{
nano::read (stream_a, previous.bytes);
nano::read (stream_a, destination.bytes);
nano::read (stream_a, balance.bytes);
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
nano::send_hashables::send_hashables (bool & error_a, boost::property_tree::ptree const & tree_a)
{
try
{
auto previous_l (tree_a.get<std::string> ("previous"));
auto destination_l (tree_a.get<std::string> ("destination"));
auto balance_l (tree_a.get<std::string> ("balance"));
error_a = previous.decode_hex (previous_l);
if (!error_a)
{
error_a = destination.decode_account (destination_l);
if (!error_a)
{
error_a = balance.decode_hex (balance_l);
}
}
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
void nano::send_hashables::hash (blake2b_state & hash_a) const
{
auto status (blake2b_update (&hash_a, previous.bytes.data (), sizeof (previous.bytes)));
debug_assert (status == 0);
status = blake2b_update (&hash_a, destination.bytes.data (), sizeof (destination.bytes));
debug_assert (status == 0);
status = blake2b_update (&hash_a, balance.bytes.data (), sizeof (balance.bytes));
debug_assert (status == 0);
}
void nano::send_block::serialize (nano::stream & stream_a) const
{
write (stream_a, hashables.previous.bytes);
write (stream_a, hashables.destination.bytes);
write (stream_a, hashables.balance.bytes);
write (stream_a, signature.bytes);
write (stream_a, work);
}
bool nano::send_block::deserialize (nano::stream & stream_a)
{
auto error (false);
try
{
read (stream_a, hashables.previous.bytes);
read (stream_a, hashables.destination.bytes);
read (stream_a, hashables.balance.bytes);
read (stream_a, signature.bytes);
read (stream_a, work);
}
catch (std::exception const &)
{
error = true;
}
return error;
}
void nano::send_block::serialize_json (std::string & string_a, bool single_line) const
{
boost::property_tree::ptree tree;
serialize_json (tree);
std::stringstream ostream;
boost::property_tree::write_json (ostream, tree, !single_line);
string_a = ostream.str ();
}
void nano::send_block::serialize_json (boost::property_tree::ptree & tree) const
{
tree.put ("type", "send");
std::string previous;
hashables.previous.encode_hex (previous);
tree.put ("previous", previous);
tree.put ("destination", hashables.destination.to_account ());
std::string balance;
hashables.balance.encode_hex (balance);
tree.put ("balance", balance);
std::string signature_l;
signature.encode_hex (signature_l);
tree.put ("work", nano::to_string_hex (work));
tree.put ("signature", signature_l);
}
bool nano::send_block::deserialize_json (boost::property_tree::ptree const & tree_a)
{
auto error (false);
try
{
debug_assert (tree_a.get<std::string> ("type") == "send");
auto previous_l (tree_a.get<std::string> ("previous"));
auto destination_l (tree_a.get<std::string> ("destination"));
auto balance_l (tree_a.get<std::string> ("balance"));
auto work_l (tree_a.get<std::string> ("work"));
auto signature_l (tree_a.get<std::string> ("signature"));
error = hashables.previous.decode_hex (previous_l);
if (!error)
{
error = hashables.destination.decode_account (destination_l);
if (!error)
{
error = hashables.balance.decode_hex (balance_l);
if (!error)
{
error = nano::from_string_hex (work_l, work);
if (!error)
{
error = signature.decode_hex (signature_l);
}
}
}
}
}
catch (std::runtime_error const &)
{
error = true;
}
return error;
}
nano::send_block::send_block (nano::block_hash const & previous_a, nano::account const & destination_a, nano::amount const & balance_a, nano::raw_key const & prv_a, nano::public_key const & pub_a, uint64_t work_a) :
hashables (previous_a, destination_a, balance_a),
signature (nano::sign_message (prv_a, pub_a, hash ())),
work (work_a)
{
debug_assert (destination_a != nullptr);
debug_assert (pub_a != nullptr);
}
nano::send_block::send_block (bool & error_a, nano::stream & stream_a) :
hashables (error_a, stream_a)
{
if (!error_a)
{
try
{
nano::read (stream_a, signature.bytes);
nano::read (stream_a, work);
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
}
nano::send_block::send_block (bool & error_a, boost::property_tree::ptree const & tree_a) :
hashables (error_a, tree_a)
{
if (!error_a)
{
try
{
auto signature_l (tree_a.get<std::string> ("signature"));
auto work_l (tree_a.get<std::string> ("work"));
error_a = signature.decode_hex (signature_l);
if (!error_a)
{
error_a = nano::from_string_hex (work_l, work);
}
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
}
std::shared_ptr<nano::block> nano::send_block::clone () const
{
return std::make_shared<nano::send_block> (*this);
}
bool nano::send_block::operator== (nano::block const & other_a) const
{
return blocks_equal (*this, other_a);
}
bool nano::send_block::valid_predecessor (nano::block const & block_a) const
{
bool result;
switch (block_a.type ())
{
case nano::block_type::send:
case nano::block_type::receive:
case nano::block_type::open:
case nano::block_type::change:
result = true;
break;
default:
result = false;
break;
}
return result;
}
nano::block_type nano::send_block::type () const
{
return nano::block_type::send;
}
bool nano::send_block::operator== (nano::send_block const & other_a) const
{
auto result (hashables.destination == other_a.hashables.destination && hashables.previous == other_a.hashables.previous && hashables.balance == other_a.hashables.balance && work == other_a.work && signature == other_a.signature);
return result;
}
std::optional<nano::block_hash> nano::send_block::previous_field () const
{
return hashables.previous;
}
std::optional<nano::account> nano::send_block::destination_field () const
{
return hashables.destination;
}
nano::root nano::send_block::root () const
{
return hashables.previous;
}
std::optional<nano::amount> nano::send_block::balance_field () const
{
return hashables.balance;
}
nano::signature const & nano::send_block::block_signature () const
{
return signature;
}
void nano::send_block::signature_set (nano::signature const & signature_a)
{
signature = signature_a;
}
void nano::send_block::operator() (nano::object_stream & obs) const
{
nano::block::operator() (obs); // Write common data
obs.write ("previous", hashables.previous);
obs.write ("destination", hashables.destination);
obs.write ("balance", hashables.balance);
obs.write ("signature", signature);
obs.write ("work", work);
}
/*
* open_block
*/
nano::open_hashables::open_hashables (nano::block_hash const & source_a, nano::account const & representative_a, nano::account const & account_a) :
source (source_a),
representative (representative_a),
account (account_a)
{
}
nano::open_hashables::open_hashables (bool & error_a, nano::stream & stream_a)
{
try
{
nano::read (stream_a, source.bytes);
nano::read (stream_a, representative.bytes);
nano::read (stream_a, account.bytes);
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
nano::open_hashables::open_hashables (bool & error_a, boost::property_tree::ptree const & tree_a)
{
try
{
auto source_l (tree_a.get<std::string> ("source"));
auto representative_l (tree_a.get<std::string> ("representative"));
auto account_l (tree_a.get<std::string> ("account"));
error_a = source.decode_hex (source_l);
if (!error_a)
{
error_a = representative.decode_account (representative_l);
if (!error_a)
{
error_a = account.decode_account (account_l);
}
}
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
void nano::open_hashables::hash (blake2b_state & hash_a) const
{
blake2b_update (&hash_a, source.bytes.data (), sizeof (source.bytes));
blake2b_update (&hash_a, representative.bytes.data (), sizeof (representative.bytes));
blake2b_update (&hash_a, account.bytes.data (), sizeof (account.bytes));
}
nano::open_block::open_block (nano::block_hash const & source_a, nano::account const & representative_a, nano::account const & account_a, nano::raw_key const & prv_a, nano::public_key const & pub_a, uint64_t work_a) :
hashables (source_a, representative_a, account_a),
signature (nano::sign_message (prv_a, pub_a, hash ())),
work (work_a)
{
debug_assert (representative_a != nullptr);
debug_assert (account_a != nullptr);
debug_assert (pub_a != nullptr);
}
nano::open_block::open_block (nano::block_hash const & source_a, nano::account const & representative_a, nano::account const & account_a, std::nullptr_t) :
hashables (source_a, representative_a, account_a),
work (0)
{
debug_assert (representative_a != nullptr);
debug_assert (account_a != nullptr);
signature.clear ();
}
nano::open_block::open_block (bool & error_a, nano::stream & stream_a) :
hashables (error_a, stream_a)
{
if (!error_a)
{
try
{
nano::read (stream_a, signature);
nano::read (stream_a, work);
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
}
nano::open_block::open_block (bool & error_a, boost::property_tree::ptree const & tree_a) :
hashables (error_a, tree_a)
{
if (!error_a)
{
try
{
auto work_l (tree_a.get<std::string> ("work"));
auto signature_l (tree_a.get<std::string> ("signature"));
error_a = nano::from_string_hex (work_l, work);
if (!error_a)
{
error_a = signature.decode_hex (signature_l);
}
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
}
std::shared_ptr<nano::block> nano::open_block::clone () const
{
return std::make_shared<nano::open_block> (*this);
}
void nano::open_block::generate_hash (blake2b_state & hash_a) const
{
hashables.hash (hash_a);
}
uint64_t nano::open_block::block_work () const
{
return work;
}
void nano::open_block::block_work_set (uint64_t work_a)
{
work = work_a;
}
std::optional<nano::block_hash> nano::open_block::previous_field () const
{
return std::nullopt;
}
std::optional<nano::account> nano::open_block::account_field () const
{
return hashables.account;
}
void nano::open_block::serialize (nano::stream & stream_a) const
{
write (stream_a, hashables.source);
write (stream_a, hashables.representative);
write (stream_a, hashables.account);
write (stream_a, signature);
write (stream_a, work);
}
bool nano::open_block::deserialize (nano::stream & stream_a)
{
auto error (false);
try
{
read (stream_a, hashables.source);
read (stream_a, hashables.representative);
read (stream_a, hashables.account);
read (stream_a, signature);
read (stream_a, work);
}
catch (std::runtime_error const &)
{
error = true;
}
return error;
}
void nano::open_block::serialize_json (std::string & string_a, bool single_line) const
{
boost::property_tree::ptree tree;
serialize_json (tree);
std::stringstream ostream;
boost::property_tree::write_json (ostream, tree, !single_line);
string_a = ostream.str ();
}
void nano::open_block::serialize_json (boost::property_tree::ptree & tree) const
{
tree.put ("type", "open");
tree.put ("source", hashables.source.to_string ());
tree.put ("representative", hashables.representative.to_account ());
tree.put ("account", hashables.account.to_account ());
std::string signature_l;
signature.encode_hex (signature_l);
tree.put ("work", nano::to_string_hex (work));
tree.put ("signature", signature_l);
}
bool nano::open_block::deserialize_json (boost::property_tree::ptree const & tree_a)
{
auto error (false);
try
{
debug_assert (tree_a.get<std::string> ("type") == "open");
auto source_l (tree_a.get<std::string> ("source"));
auto representative_l (tree_a.get<std::string> ("representative"));
auto account_l (tree_a.get<std::string> ("account"));
auto work_l (tree_a.get<std::string> ("work"));
auto signature_l (tree_a.get<std::string> ("signature"));
error = hashables.source.decode_hex (source_l);
if (!error)
{
error = hashables.representative.decode_hex (representative_l);
if (!error)
{
error = hashables.account.decode_hex (account_l);
if (!error)
{
error = nano::from_string_hex (work_l, work);
if (!error)
{
error = signature.decode_hex (signature_l);
}
}
}
}
}
catch (std::runtime_error const &)
{
error = true;
}
return error;
}
void nano::open_block::visit (nano::block_visitor & visitor_a) const
{
visitor_a.open_block (*this);
}
void nano::open_block::visit (nano::mutable_block_visitor & visitor_a)
{
visitor_a.open_block (*this);
}
nano::block_type nano::open_block::type () const
{
return nano::block_type::open;
}
bool nano::open_block::operator== (nano::block const & other_a) const
{
return blocks_equal (*this, other_a);
}
bool nano::open_block::operator== (nano::open_block const & other_a) const
{
return hashables.source == other_a.hashables.source && hashables.representative == other_a.hashables.representative && hashables.account == other_a.hashables.account && work == other_a.work && signature == other_a.signature;
}
bool nano::open_block::valid_predecessor (nano::block const & block_a) const
{
return false;
}
std::optional<nano::block_hash> nano::open_block::source_field () const
{
return hashables.source;
}
nano::root nano::open_block::root () const
{
return hashables.account;
}
std::optional<nano::account> nano::open_block::representative_field () const
{
return hashables.representative;
}
nano::signature const & nano::open_block::block_signature () const
{
return signature;
}
void nano::open_block::signature_set (nano::signature const & signature_a)
{
signature = signature_a;
}
void nano::open_block::operator() (nano::object_stream & obs) const
{
nano::block::operator() (obs); // Write common data
obs.write ("source", hashables.source);
obs.write ("representative", hashables.representative);
obs.write ("account", hashables.account);
obs.write ("signature", signature);
obs.write ("work", work);
}
/*
* change_block
*/
nano::change_hashables::change_hashables (nano::block_hash const & previous_a, nano::account const & representative_a) :
previous (previous_a),
representative (representative_a)
{
}
nano::change_hashables::change_hashables (bool & error_a, nano::stream & stream_a)
{
try
{
nano::read (stream_a, previous);
nano::read (stream_a, representative);
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
nano::change_hashables::change_hashables (bool & error_a, boost::property_tree::ptree const & tree_a)
{
try
{
auto previous_l (tree_a.get<std::string> ("previous"));
auto representative_l (tree_a.get<std::string> ("representative"));
error_a = previous.decode_hex (previous_l);
if (!error_a)
{
error_a = representative.decode_account (representative_l);
}
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
void nano::change_hashables::hash (blake2b_state & hash_a) const
{
blake2b_update (&hash_a, previous.bytes.data (), sizeof (previous.bytes));
blake2b_update (&hash_a, representative.bytes.data (), sizeof (representative.bytes));
}
nano::change_block::change_block (nano::block_hash const & previous_a, nano::account const & representative_a, nano::raw_key const & prv_a, nano::public_key const & pub_a, uint64_t work_a) :
hashables (previous_a, representative_a),
signature (nano::sign_message (prv_a, pub_a, hash ())),
work (work_a)
{
debug_assert (representative_a != nullptr);
debug_assert (pub_a != nullptr);
}
nano::change_block::change_block (bool & error_a, nano::stream & stream_a) :
hashables (error_a, stream_a)
{
if (!error_a)
{
try
{
nano::read (stream_a, signature);
nano::read (stream_a, work);
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
}
nano::change_block::change_block (bool & error_a, boost::property_tree::ptree const & tree_a) :
hashables (error_a, tree_a)
{
if (!error_a)
{
try
{
auto work_l (tree_a.get<std::string> ("work"));
auto signature_l (tree_a.get<std::string> ("signature"));
error_a = nano::from_string_hex (work_l, work);
if (!error_a)
{
error_a = signature.decode_hex (signature_l);
}
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
}
std::shared_ptr<nano::block> nano::change_block::clone () const
{
return std::make_shared<nano::change_block> (*this);
}
void nano::change_block::generate_hash (blake2b_state & hash_a) const
{
hashables.hash (hash_a);
}
uint64_t nano::change_block::block_work () const
{
return work;
}
void nano::change_block::block_work_set (uint64_t work_a)
{
work = work_a;
}
std::optional<nano::block_hash> nano::change_block::previous_field () const
{
return hashables.previous;
}
void nano::change_block::serialize (nano::stream & stream_a) const
{
write (stream_a, hashables.previous);
write (stream_a, hashables.representative);
write (stream_a, signature);
write (stream_a, work);
}
bool nano::change_block::deserialize (nano::stream & stream_a)
{
auto error (false);
try
{
read (stream_a, hashables.previous);
read (stream_a, hashables.representative);
read (stream_a, signature);
read (stream_a, work);
}
catch (std::runtime_error const &)
{
error = true;
}
return error;
}
void nano::change_block::serialize_json (std::string & string_a, bool single_line) const
{
boost::property_tree::ptree tree;
serialize_json (tree);
std::stringstream ostream;
boost::property_tree::write_json (ostream, tree, !single_line);
string_a = ostream.str ();
}
void nano::change_block::serialize_json (boost::property_tree::ptree & tree) const
{
tree.put ("type", "change");
tree.put ("previous", hashables.previous.to_string ());
tree.put ("representative", hashables.representative.to_account ());
tree.put ("work", nano::to_string_hex (work));
std::string signature_l;
signature.encode_hex (signature_l);
tree.put ("signature", signature_l);
}
bool nano::change_block::deserialize_json (boost::property_tree::ptree const & tree_a)
{
auto error (false);
try
{
debug_assert (tree_a.get<std::string> ("type") == "change");
auto previous_l (tree_a.get<std::string> ("previous"));
auto representative_l (tree_a.get<std::string> ("representative"));
auto work_l (tree_a.get<std::string> ("work"));
auto signature_l (tree_a.get<std::string> ("signature"));
error = hashables.previous.decode_hex (previous_l);
if (!error)
{
error = hashables.representative.decode_hex (representative_l);
if (!error)
{
error = nano::from_string_hex (work_l, work);
if (!error)
{
error = signature.decode_hex (signature_l);
}
}
}
}
catch (std::runtime_error const &)
{
error = true;
}
return error;
}
void nano::change_block::visit (nano::block_visitor & visitor_a) const
{
visitor_a.change_block (*this);
}
void nano::change_block::visit (nano::mutable_block_visitor & visitor_a)
{
visitor_a.change_block (*this);
}
nano::block_type nano::change_block::type () const
{
return nano::block_type::change;
}
bool nano::change_block::operator== (nano::block const & other_a) const
{
return blocks_equal (*this, other_a);
}
bool nano::change_block::operator== (nano::change_block const & other_a) const
{
return hashables.previous == other_a.hashables.previous && hashables.representative == other_a.hashables.representative && work == other_a.work && signature == other_a.signature;
}
bool nano::change_block::valid_predecessor (nano::block const & block_a) const
{
bool result;
switch (block_a.type ())
{
case nano::block_type::send:
case nano::block_type::receive:
case nano::block_type::open:
case nano::block_type::change:
result = true;
break;
default:
result = false;
break;
}
return result;
}
nano::root nano::change_block::root () const
{
return hashables.previous;
}
std::optional<nano::account> nano::change_block::representative_field () const
{
return hashables.representative;
}
nano::signature const & nano::change_block::block_signature () const
{
return signature;
}
void nano::change_block::signature_set (nano::signature const & signature_a)
{
signature = signature_a;
}
void nano::change_block::operator() (nano::object_stream & obs) const
{
nano::block::operator() (obs); // Write common data
obs.write ("previous", hashables.previous);
obs.write ("representative", hashables.representative);
obs.write ("signature", signature);
obs.write ("work", work);
}
/*
* state_block
*/
nano::state_hashables::state_hashables (nano::account const & account_a, nano::block_hash const & previous_a, nano::account const & representative_a, nano::amount const & balance_a, nano::link const & link_a) :
account (account_a),
previous (previous_a),
representative (representative_a),
balance (balance_a),
link (link_a)
{
}
nano::state_hashables::state_hashables (bool & error_a, nano::stream & stream_a)
{
try
{
nano::read (stream_a, account);
nano::read (stream_a, previous);
nano::read (stream_a, representative);
nano::read (stream_a, balance);
nano::read (stream_a, link);
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
nano::state_hashables::state_hashables (bool & error_a, boost::property_tree::ptree const & tree_a)
{
try
{
auto account_l (tree_a.get<std::string> ("account"));
auto previous_l (tree_a.get<std::string> ("previous"));
auto representative_l (tree_a.get<std::string> ("representative"));
auto balance_l (tree_a.get<std::string> ("balance"));
auto link_l (tree_a.get<std::string> ("link"));
error_a = account.decode_account (account_l);
if (!error_a)
{
error_a = previous.decode_hex (previous_l);
if (!error_a)
{
error_a = representative.decode_account (representative_l);
if (!error_a)
{
error_a = balance.decode_dec (balance_l);
if (!error_a)
{
error_a = link.decode_account (link_l) && link.decode_hex (link_l);
}
}
}
}
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
void nano::state_hashables::hash (blake2b_state & hash_a) const
{
blake2b_update (&hash_a, account.bytes.data (), sizeof (account.bytes));
blake2b_update (&hash_a, previous.bytes.data (), sizeof (previous.bytes));
blake2b_update (&hash_a, representative.bytes.data (), sizeof (representative.bytes));
blake2b_update (&hash_a, balance.bytes.data (), sizeof (balance.bytes));
blake2b_update (&hash_a, link.bytes.data (), sizeof (link.bytes));
}
nano::state_block::state_block (nano::account const & account_a, nano::block_hash const & previous_a, nano::account const & representative_a, nano::amount const & balance_a, nano::link const & link_a, nano::raw_key const & prv_a, nano::public_key const & pub_a, uint64_t work_a) :
hashables (account_a, previous_a, representative_a, balance_a, link_a),
signature (nano::sign_message (prv_a, pub_a, hash ())),
work (work_a)
{
debug_assert (account_a != nullptr);
debug_assert (representative_a != nullptr);
debug_assert (link_a.as_account () != nullptr);
debug_assert (pub_a != nullptr);
}
nano::state_block::state_block (bool & error_a, nano::stream & stream_a) :
hashables (error_a, stream_a)
{
if (!error_a)
{
try
{
nano::read (stream_a, signature);
nano::read (stream_a, work);
boost::endian::big_to_native_inplace (work);
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
}
nano::state_block::state_block (bool & error_a, boost::property_tree::ptree const & tree_a) :
hashables (error_a, tree_a)
{
if (!error_a)
{
try
{
auto type_l (tree_a.get<std::string> ("type"));
auto signature_l (tree_a.get<std::string> ("signature"));
auto work_l (tree_a.get<std::string> ("work"));
error_a = type_l != "state";
if (!error_a)
{
error_a = nano::from_string_hex (work_l, work);
if (!error_a)
{
error_a = signature.decode_hex (signature_l);
}
}
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
}
std::shared_ptr<nano::block> nano::state_block::clone () const
{
return std::make_shared<nano::state_block> (*this);
}
void nano::state_block::generate_hash (blake2b_state & hash_a) const
{
nano::uint256_union preamble (static_cast<uint64_t> (nano::block_type::state));
blake2b_update (&hash_a, preamble.bytes.data (), preamble.bytes.size ());
hashables.hash (hash_a);
}
uint64_t nano::state_block::block_work () const
{
return work;
}
void nano::state_block::block_work_set (uint64_t work_a)
{
work = work_a;
}
std::optional<nano::block_hash> nano::state_block::previous_field () const
{
return hashables.previous;
}
std::optional<nano::account> nano::state_block::account_field () const
{
return hashables.account;
}
void nano::state_block::serialize (nano::stream & stream_a) const
{
write (stream_a, hashables.account);
write (stream_a, hashables.previous);
write (stream_a, hashables.representative);
write (stream_a, hashables.balance);
write (stream_a, hashables.link);
write (stream_a, signature);
write (stream_a, boost::endian::native_to_big (work));
}
bool nano::state_block::deserialize (nano::stream & stream_a)
{
auto error (false);
try
{
read (stream_a, hashables.account);
read (stream_a, hashables.previous);
read (stream_a, hashables.representative);
read (stream_a, hashables.balance);
read (stream_a, hashables.link);
read (stream_a, signature);
read (stream_a, work);
boost::endian::big_to_native_inplace (work);
}
catch (std::runtime_error const &)
{
error = true;
}
return error;
}
void nano::state_block::serialize_json (std::string & string_a, bool single_line) const
{
boost::property_tree::ptree tree;
serialize_json (tree);
std::stringstream ostream;
boost::property_tree::write_json (ostream, tree, !single_line);
string_a = ostream.str ();
}
void nano::state_block::serialize_json (boost::property_tree::ptree & tree) const
{
tree.put ("type", "state");
tree.put ("account", hashables.account.to_account ());
tree.put ("previous", hashables.previous.to_string ());
tree.put ("representative", hashables.representative.to_account ());
tree.put ("balance", hashables.balance.to_string_dec ());
tree.put ("link", hashables.link.to_string ());
tree.put ("link_as_account", hashables.link.to_account ());
std::string signature_l;
signature.encode_hex (signature_l);
tree.put ("signature", signature_l);
tree.put ("work", nano::to_string_hex (work));
}
bool nano::state_block::deserialize_json (boost::property_tree::ptree const & tree_a)
{
auto error (false);
try
{
debug_assert (tree_a.get<std::string> ("type") == "state");
auto account_l (tree_a.get<std::string> ("account"));
auto previous_l (tree_a.get<std::string> ("previous"));
auto representative_l (tree_a.get<std::string> ("representative"));
auto balance_l (tree_a.get<std::string> ("balance"));
auto link_l (tree_a.get<std::string> ("link"));
auto work_l (tree_a.get<std::string> ("work"));
auto signature_l (tree_a.get<std::string> ("signature"));
error = hashables.account.decode_account (account_l);
if (!error)
{
error = hashables.previous.decode_hex (previous_l);
if (!error)
{
error = hashables.representative.decode_account (representative_l);
if (!error)
{
error = hashables.balance.decode_dec (balance_l);
if (!error)
{
error = hashables.link.decode_account (link_l) && hashables.link.decode_hex (link_l);
if (!error)
{
error = nano::from_string_hex (work_l, work);
if (!error)
{
error = signature.decode_hex (signature_l);
}
}
}
}
}
}
}
catch (std::runtime_error const &)
{
error = true;
}
return error;
}
void nano::state_block::visit (nano::block_visitor & visitor_a) const
{
visitor_a.state_block (*this);
}
void nano::state_block::visit (nano::mutable_block_visitor & visitor_a)
{
visitor_a.state_block (*this);
}
nano::block_type nano::state_block::type () const
{
return nano::block_type::state;
}
bool nano::state_block::operator== (nano::block const & other_a) const
{
return blocks_equal (*this, other_a);
}
bool nano::state_block::operator== (nano::state_block const & other_a) const
{
return hashables.account == other_a.hashables.account && hashables.previous == other_a.hashables.previous && hashables.representative == other_a.hashables.representative && hashables.balance == other_a.hashables.balance && hashables.link == other_a.hashables.link && signature == other_a.signature && work == other_a.work;
}
bool nano::state_block::valid_predecessor (nano::block const & block_a) const
{
return true;
}
nano::root nano::state_block::root () const
{
if (!hashables.previous.is_zero ())
{
return hashables.previous;
}
else
{
return hashables.account;
}
}
std::optional<nano::link> nano::state_block::link_field () const
{
return hashables.link;
}
std::optional<nano::account> nano::state_block::representative_field () const
{
return hashables.representative;
}
std::optional<nano::amount> nano::state_block::balance_field () const
{
return hashables.balance;
}
nano::signature const & nano::state_block::block_signature () const
{
return signature;
}
void nano::state_block::signature_set (nano::signature const & signature_a)
{
signature = signature_a;
}
void nano::state_block::operator() (nano::object_stream & obs) const
{
nano::block::operator() (obs); // Write common data
obs.write ("account", hashables.account);
obs.write ("previous", hashables.previous);
obs.write ("representative", hashables.representative);
obs.write ("balance", hashables.balance);
obs.write ("link", hashables.link);
obs.write ("signature", signature);
obs.write ("work", work);
}
/*
*
*/
std::shared_ptr<nano::block> nano::deserialize_block_json (boost::property_tree::ptree const & tree_a, nano::block_uniquer * uniquer_a)
{
std::shared_ptr<nano::block> result;
try
{
auto type (tree_a.get<std::string> ("type"));
bool error (false);
std::unique_ptr<nano::block> obj;
if (type == "receive")
{
obj = std::make_unique<nano::receive_block> (error, tree_a);
}
else if (type == "send")
{
obj = std::make_unique<nano::send_block> (error, tree_a);
}
else if (type == "open")
{
obj = std::make_unique<nano::open_block> (error, tree_a);
}
else if (type == "change")
{
obj = std::make_unique<nano::change_block> (error, tree_a);
}
else if (type == "state")
{
obj = std::make_unique<nano::state_block> (error, tree_a);
}
if (!error)
{
result = std::move (obj);
}
}
catch (std::runtime_error const &)
{
}
if (uniquer_a != nullptr)
{
result = uniquer_a->unique (result);
}
return result;
}
void nano::serialize_block (nano::stream & stream_a, nano::block const & block_a)
{
nano::write (stream_a, block_a.type ());
block_a.serialize (stream_a);
}
std::shared_ptr<nano::block> nano::deserialize_block (nano::stream & stream_a)
{
nano::block_type type;
auto error (try_read (stream_a, type));
std::shared_ptr<nano::block> result;
if (!error)
{
result = nano::deserialize_block (stream_a, type);
}
return result;
}
std::shared_ptr<nano::block> nano::deserialize_block (nano::stream & stream_a, nano::block_type type_a, nano::block_uniquer * uniquer_a)
{
std::shared_ptr<nano::block> result;
switch (type_a)
{
case nano::block_type::receive:
{
result = ::deserialize_block<nano::receive_block> (stream_a);
break;
}
case nano::block_type::send:
{
result = ::deserialize_block<nano::send_block> (stream_a);
break;
}
case nano::block_type::open:
{
result = ::deserialize_block<nano::open_block> (stream_a);
break;
}
case nano::block_type::change:
{
result = ::deserialize_block<nano::change_block> (stream_a);
break;
}
case nano::block_type::state:
{
result = ::deserialize_block<nano::state_block> (stream_a);
break;
}
default:
{
return {};
}
}
if (result && uniquer_a != nullptr)
{
result = uniquer_a->unique (result);
}
return result;
}
/*
* receive_block
*/
void nano::receive_block::visit (nano::block_visitor & visitor_a) const
{
visitor_a.receive_block (*this);
}
void nano::receive_block::visit (nano::mutable_block_visitor & visitor_a)
{
visitor_a.receive_block (*this);
}
bool nano::receive_block::operator== (nano::receive_block const & other_a) const
{
auto result (hashables.previous == other_a.hashables.previous && hashables.source == other_a.hashables.source && work == other_a.work && signature == other_a.signature);
return result;
}
void nano::receive_block::serialize (nano::stream & stream_a) const
{
write (stream_a, hashables.previous.bytes);
write (stream_a, hashables.source.bytes);
write (stream_a, signature.bytes);
write (stream_a, work);
}
bool nano::receive_block::deserialize (nano::stream & stream_a)
{
auto error (false);
try
{
read (stream_a, hashables.previous.bytes);
read (stream_a, hashables.source.bytes);
read (stream_a, signature.bytes);
read (stream_a, work);
}
catch (std::runtime_error const &)
{
error = true;
}
return error;
}
void nano::receive_block::serialize_json (std::string & string_a, bool single_line) const
{
boost::property_tree::ptree tree;
serialize_json (tree);
std::stringstream ostream;
boost::property_tree::write_json (ostream, tree, !single_line);
string_a = ostream.str ();
}
void nano::receive_block::serialize_json (boost::property_tree::ptree & tree) const
{
tree.put ("type", "receive");
std::string previous;
hashables.previous.encode_hex (previous);
tree.put ("previous", previous);
std::string source;
hashables.source.encode_hex (source);
tree.put ("source", source);
std::string signature_l;
signature.encode_hex (signature_l);
tree.put ("work", nano::to_string_hex (work));
tree.put ("signature", signature_l);
}
bool nano::receive_block::deserialize_json (boost::property_tree::ptree const & tree_a)
{
auto error (false);
try
{
debug_assert (tree_a.get<std::string> ("type") == "receive");
auto previous_l (tree_a.get<std::string> ("previous"));
auto source_l (tree_a.get<std::string> ("source"));
auto work_l (tree_a.get<std::string> ("work"));
auto signature_l (tree_a.get<std::string> ("signature"));
error = hashables.previous.decode_hex (previous_l);
if (!error)
{
error = hashables.source.decode_hex (source_l);
if (!error)
{
error = nano::from_string_hex (work_l, work);
if (!error)
{
error = signature.decode_hex (signature_l);
}
}
}
}
catch (std::runtime_error const &)
{
error = true;
}
return error;
}
nano::receive_block::receive_block (nano::block_hash const & previous_a, nano::block_hash const & source_a, nano::raw_key const & prv_a, nano::public_key const & pub_a, uint64_t work_a) :
hashables (previous_a, source_a),
signature (nano::sign_message (prv_a, pub_a, hash ())),
work (work_a)
{
debug_assert (pub_a != nullptr);
}
nano::receive_block::receive_block (bool & error_a, nano::stream & stream_a) :
hashables (error_a, stream_a)
{
if (!error_a)
{
try
{
nano::read (stream_a, signature);
nano::read (stream_a, work);
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
}
nano::receive_block::receive_block (bool & error_a, boost::property_tree::ptree const & tree_a) :
hashables (error_a, tree_a)
{
if (!error_a)
{
try
{
auto signature_l (tree_a.get<std::string> ("signature"));
auto work_l (tree_a.get<std::string> ("work"));
error_a = signature.decode_hex (signature_l);
if (!error_a)
{
error_a = nano::from_string_hex (work_l, work);
}
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
}
std::shared_ptr<nano::block> nano::receive_block::clone () const
{
return std::make_shared<nano::receive_block> (*this);
}
void nano::receive_block::generate_hash (blake2b_state & hash_a) const
{
hashables.hash (hash_a);
}
uint64_t nano::receive_block::block_work () const
{
return work;
}
void nano::receive_block::block_work_set (uint64_t work_a)
{
work = work_a;
}
bool nano::receive_block::operator== (nano::block const & other_a) const
{
return blocks_equal (*this, other_a);
}
bool nano::receive_block::valid_predecessor (nano::block const & block_a) const
{
bool result;
switch (block_a.type ())
{
case nano::block_type::send:
case nano::block_type::receive:
case nano::block_type::open:
case nano::block_type::change:
result = true;
break;
default:
result = false;
break;
}
return result;
}
std::optional<nano::block_hash> nano::receive_block::previous_field () const
{
return hashables.previous;
}
std::optional<nano::block_hash> nano::receive_block::source_field () const
{
return hashables.source;
}
nano::root nano::receive_block::root () const
{
return hashables.previous;
}
nano::signature const & nano::receive_block::block_signature () const
{
return signature;
}
void nano::receive_block::signature_set (nano::signature const & signature_a)
{
signature = signature_a;
}
nano::block_type nano::receive_block::type () const
{
return nano::block_type::receive;
}
nano::receive_hashables::receive_hashables (nano::block_hash const & previous_a, nano::block_hash const & source_a) :
previous (previous_a),
source (source_a)
{
}
nano::receive_hashables::receive_hashables (bool & error_a, nano::stream & stream_a)
{
try
{
nano::read (stream_a, previous.bytes);
nano::read (stream_a, source.bytes);
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
nano::receive_hashables::receive_hashables (bool & error_a, boost::property_tree::ptree const & tree_a)
{
try
{
auto previous_l (tree_a.get<std::string> ("previous"));
auto source_l (tree_a.get<std::string> ("source"));
error_a = previous.decode_hex (previous_l);
if (!error_a)
{
error_a = source.decode_hex (source_l);
}
}
catch (std::runtime_error const &)
{
error_a = true;
}
}
void nano::receive_hashables::hash (blake2b_state & hash_a) const
{
blake2b_update (&hash_a, previous.bytes.data (), sizeof (previous.bytes));
blake2b_update (&hash_a, source.bytes.data (), sizeof (source.bytes));
}
void nano::receive_block::operator() (nano::object_stream & obs) const
{
nano::block::operator() (obs); // Write common data
obs.write ("previous", hashables.previous);
obs.write ("source", hashables.source);
obs.write ("signature", signature);
obs.write ("work", work);
}