This commit is contained in:
Piotr Wójcik 2024-01-22 20:51:21 +01:00
commit 18749c26ce
41 changed files with 2216 additions and 257 deletions

View file

@ -133,6 +133,12 @@ if(NANO_STACKTRACE_BACKTRACE)
endif()
endif()
option(NANO_TRACING "Enable trace logging" OFF)
if(NANO_TRACING)
message(STATUS "Using trace logging")
add_compile_definitions(NANO_TRACING)
endif()
if(${NANO_TIMED_LOCKS} GREATER 0)
add_definitions(-DNANO_TIMED_LOCKS=${NANO_TIMED_LOCKS})
add_definitions(-DNANO_TIMED_LOCKS_FILTER=${NANO_TIMED_LOCKS_FILTER})

View file

@ -57,6 +57,7 @@ cmake \
-DACTIVE_NETWORK=nano_${NANO_NETWORK:-"live"}_network \
-DNANO_TEST=${NANO_TEST:-OFF} \
-DNANO_GUI=${NANO_GUI:-OFF} \
-DNANO_TRACING=${NANO_TRACING:-OFF} \
-DCOVERAGE=${COVERAGE:-OFF} \
-DCI_TAG=${CI_TAG:-OFF} \
-DCI_VERSION_PRE_RELEASE=${CI_VERSION_PRE_RELEASE:-OFF} \

View file

@ -26,13 +26,14 @@ add_executable(
ipc.cpp
ledger.cpp
locks.cpp
logger.cpp
logging.cpp
message.cpp
message_deserializer.cpp
memory_pool.cpp
network.cpp
network_filter.cpp
node.cpp
object_stream.cpp
optimistic_scheduler.cpp
processing_queue.cpp
processor_service.cpp

View file

@ -303,6 +303,6 @@ TEST (election, continuous_voting)
ASSERT_TRUE (nano::test::process (node1, { send2 }));
ASSERT_TIMELY (5s, node1.active.active (*send2));
// Ensure votes are generated in continuous manner
ASSERT_TIMELY (5s, node1.stats.count (nano::stat::type::election, nano::stat::detail::generate_vote) >= 5);
// Ensure votes are broadcasted in continuous manner
ASSERT_TIMELY (5s, node1.stats.count (nano::stat::type::election, nano::stat::detail::broadcast_vote) >= 5);
}

View file

@ -1,16 +0,0 @@
#include <nano/secure/utility.hpp>
#include <nano/test_common/testutil.hpp>
#include <gtest/gtest.h>
#include <chrono>
#include <regex>
#include <thread>
using namespace std::chrono_literals;
TEST (logger, basic)
{
// TODO
}

106
nano/core_test/logging.cpp Normal file
View file

@ -0,0 +1,106 @@
#include <nano/lib/logging.hpp>
#include <nano/test_common/testutil.hpp>
#include <gtest/gtest.h>
#include <ostream>
using namespace std::chrono_literals;
namespace
{
struct non_copyable
{
non_copyable () = default;
non_copyable (non_copyable const &) = delete;
non_copyable (non_copyable &&) = default;
non_copyable & operator= (non_copyable const &) = delete;
non_copyable & operator= (non_copyable &&) = default;
friend std::ostream & operator<< (std::ostream & os, non_copyable const & nc)
{
os << "non_copyable";
return os;
}
};
}
TEST (tracing, no_copy)
{
non_copyable nc;
nano::logger logger;
logger.trace (nano::log::type::test, nano::log::detail::test, nano::log::arg{ "non_copyable", nc });
}
namespace
{
struct non_moveable
{
non_moveable () = default;
non_moveable (non_moveable const &) = delete;
non_moveable (non_moveable &&) = delete;
non_moveable & operator= (non_moveable const &) = delete;
non_moveable & operator= (non_moveable &&) = delete;
friend std::ostream & operator<< (std::ostream & os, non_moveable const & nm)
{
os << "non_moveable";
return os;
}
};
}
TEST (tracing, no_move)
{
non_moveable nm;
nano::logger logger;
logger.trace (nano::log::type::test, nano::log::detail::test, nano::log::arg{ "non_moveable", nm });
}
TEST (log_parse, parse_level)
{
ASSERT_EQ (nano::log::parse_level ("error"), nano::log::level::error);
ASSERT_EQ (nano::log::parse_level ("off"), nano::log::level::off);
ASSERT_THROW (nano::log::parse_level ("enumnotpresent"), std::invalid_argument);
ASSERT_THROW (nano::log::parse_level (""), std::invalid_argument);
ASSERT_THROW (nano::log::parse_level ("_last"), std::invalid_argument);
ASSERT_THROW (nano::log::parse_level ("_error"), std::invalid_argument);
}
TEST (log_parse, parse_type)
{
ASSERT_EQ (nano::log::parse_type ("node"), nano::log::type::node);
ASSERT_THROW (nano::log::parse_type ("enumnotpresent"), std::invalid_argument);
ASSERT_THROW (nano::log::parse_type (""), std::invalid_argument);
ASSERT_THROW (nano::log::parse_type ("_last"), std::invalid_argument);
ASSERT_THROW (nano::log::parse_type ("_node"), std::invalid_argument);
}
TEST (log_parse, parse_detail)
{
ASSERT_EQ (nano::log::parse_detail ("all"), nano::log::detail::all);
ASSERT_EQ (nano::log::parse_detail ("process_confirmed"), nano::log::detail::process_confirmed);
ASSERT_THROW (nano::log::parse_detail ("enumnotpresent"), std::invalid_argument);
ASSERT_THROW (nano::log::parse_detail (""), std::invalid_argument);
ASSERT_THROW (nano::log::parse_detail ("_last"), std::invalid_argument);
ASSERT_THROW (nano::log::parse_detail ("_all"), std::invalid_argument);
}
TEST (log_parse, parse_logger_id)
{
ASSERT_EQ (nano::log::parse_logger_id ("node"), std::make_pair (nano::log::type::node, nano::log::detail::all));
ASSERT_EQ (nano::log::parse_logger_id ("node::all"), std::make_pair (nano::log::type::node, nano::log::detail::all));
ASSERT_EQ (nano::log::parse_logger_id ("node::process_confirmed"), std::make_pair (nano::log::type::node, nano::log::detail::process_confirmed));
ASSERT_THROW (nano::log::parse_logger_id ("_last"), std::invalid_argument);
ASSERT_THROW (nano::log::parse_logger_id ("node::enumnotpresent"), std::invalid_argument);
ASSERT_THROW (nano::log::parse_logger_id ("node::"), std::invalid_argument);
ASSERT_THROW (nano::log::parse_logger_id ("node::_all"), std::invalid_argument);
ASSERT_THROW (nano::log::parse_logger_id ("enumnotpresent"), std::invalid_argument);
ASSERT_THROW (nano::log::parse_logger_id ("invalid."), std::invalid_argument);
ASSERT_THROW (nano::log::parse_logger_id ("invalid._all"), std::invalid_argument);
ASSERT_THROW (nano::log::parse_logger_id ("::"), std::invalid_argument);
ASSERT_THROW (nano::log::parse_logger_id ("::all"), std::invalid_argument);
ASSERT_THROW (nano::log::parse_logger_id (""), std::invalid_argument);
}

View file

@ -0,0 +1,559 @@
#include <nano/lib/numbers.hpp>
#include <nano/lib/object_stream.hpp>
#include <nano/lib/object_stream_adapters.hpp>
#include <nano/test_common/testutil.hpp>
#include <gtest/gtest.h>
#include <boost/algorithm/string.hpp>
#include <cstdint>
#include <limits>
#include <sstream>
#include <string>
#include <string_view>
#include <fmt/printf.h>
namespace
{
std::string trim (std::string_view str)
{
return boost::trim_copy (std::string{ str });
}
}
TEST (object_stream, primitive_string)
{
std::stringstream ss;
nano::object_stream obs{ ss };
obs.write ("field_name_1", "field_value");
auto expected = R"(field_name_1: "field_value")";
ASSERT_EQ (ss.str (), expected);
}
TEST (object_stream, primitive_string_view)
{
std::stringstream ss;
nano::object_stream obs{ ss };
obs.write ("field_name_1", std::string_view{ "field_value" });
auto expected = R"(field_name_1: "field_value")";
ASSERT_EQ (ss.str (), expected);
}
TEST (object_stream, primitive_char)
{
std::stringstream ss;
nano::object_stream obs{ ss };
obs.write ("field_name_1", 'a');
auto expected = R"(field_name_1: "a")";
ASSERT_EQ (ss.str (), expected);
}
TEST (object_stream, primitive_bool)
{
std::stringstream ss;
nano::object_stream obs{ ss };
obs.write ("bool_field_1", true);
obs.write ("bool_field_2", false);
auto expected = trim (R"(
bool_field_1: true,
bool_field_2: false
)");
ASSERT_EQ (ss.str (), expected);
}
TEST (object_stream, primitive_int)
{
std::stringstream ss;
nano::object_stream obs{ ss };
obs.write ("int_field_1", 1234);
obs.write ("int_field_2", -1234);
obs.write ("int_field_3", std::numeric_limits<int>::max ());
obs.write ("int_field_4", std::numeric_limits<int>::min ());
auto expected = trim (R"(
int_field_1: 1234,
int_field_2: -1234,
int_field_3: 2147483647,
int_field_4: -2147483648
)");
ASSERT_EQ (ss.str (), expected);
}
TEST (object_stream, primitive_uint)
{
std::stringstream ss;
nano::object_stream obs{ ss };
obs.write ("uint_field_1", static_cast<unsigned int> (1234));
obs.write ("uint_field_2", static_cast<unsigned int> (-1234));
obs.write ("uint_field_3", std::numeric_limits<unsigned int>::max ());
obs.write ("uint_field_4", std::numeric_limits<unsigned int>::min ());
auto expected = trim (R"(
uint_field_1: 1234,
uint_field_2: 4294966062,
uint_field_3: 4294967295,
uint_field_4: 0
)");
ASSERT_EQ (ss.str (), expected);
}
TEST (object_stream, primitive_uint64)
{
std::stringstream ss;
nano::object_stream obs{ ss };
obs.write ("uint64_field_1", static_cast<uint64_t> (1234));
obs.write ("uint64_field_2", static_cast<uint64_t> (-1234));
obs.write ("uint64_field_3", std::numeric_limits<uint64_t>::max ());
obs.write ("uint64_field_4", std::numeric_limits<uint64_t>::min ());
auto expected = trim (R"(
uint64_field_1: 1234,
uint64_field_2: 18446744073709550382,
uint64_field_3: 18446744073709551615,
uint64_field_4: 0
)");
ASSERT_EQ (ss.str (), expected);
}
TEST (object_stream, primitive_int8)
{
std::stringstream ss;
nano::object_stream obs{ ss };
obs.write ("int8_field_1", static_cast<int8_t> (123));
auto expected = R"(int8_field_1: 123)";
ASSERT_EQ (ss.str (), expected);
}
TEST (object_stream, primitive_uint8)
{
std::stringstream ss;
nano::object_stream obs{ ss };
obs.write ("uint8_field_1", static_cast<uint8_t> (123));
auto expected = R"(uint8_field_1: 123)";
ASSERT_EQ (ss.str (), expected);
}
TEST (object_stream, primitive_float)
{
std::stringstream ss;
nano::object_stream obs{ ss };
obs.write ("float_field_1", 1234.5678f);
obs.write ("float_field_2", -1234.5678f);
obs.write ("float_field_3", std::numeric_limits<float>::max ());
obs.write ("float_field_4", std::numeric_limits<float>::min ());
obs.write ("float_field_5", std::numeric_limits<float>::lowest ());
auto expected = trim (R"(
float_field_1: 1234.57,
float_field_2: -1234.57,
float_field_3: 340282346638528859811704183484516925440.00,
float_field_4: 0.00,
float_field_5: -340282346638528859811704183484516925440.00
)");
ASSERT_EQ (ss.str (), expected);
}
TEST (object_stream, primitive_double)
{
std::stringstream ss;
nano::object_stream obs{ ss };
obs.write ("double_field_1", 1234.5678f);
obs.write ("double_field_2", -1234.5678f);
obs.write ("double_field_3", std::numeric_limits<double>::max ());
obs.write ("double_field_4", std::numeric_limits<double>::min ());
obs.write ("double_field_5", std::numeric_limits<double>::lowest ());
auto expected = trim (R"(
double_field_1: 1234.57,
double_field_2: -1234.57,
double_field_3: 179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00,
double_field_4: 0.00,
double_field_5: -179769313486231570814527423731704356798070567525844996598917476803157260780028538760589558632766878171540458953514382464234321326889464182768467546703537516986049910576551282076245490090389328944075868508455133942304583236903222948165808559332123348274797826204144723168738177180919299881250404026184124858368.00
)");
ASSERT_EQ (ss.str (), expected);
}
TEST (object_stream, object_writer_basic)
{
std::stringstream ss;
nano::object_stream obs{ ss };
obs.write ("object_field", [] (nano::object_stream & obs) {
obs.write ("field1", "value1");
obs.write ("field2", "value2");
obs.write ("field3", true);
obs.write ("field4", 1234);
});
auto expected = trim (R"(
object_field: {
field1: "value1",
field2: "value2",
field3: true,
field4: 1234
}
)");
ASSERT_EQ (ss.str (), expected);
}
TEST (object_stream, object_writer_nested)
{
std::stringstream ss;
nano::object_stream obs{ ss };
obs.write ("object_field", [] (nano::object_stream & obs) {
obs.write ("field1", "value1");
obs.write ("nested_object", [] (nano::object_stream & obs) {
obs.write ("nested_field1", "nested_value1");
obs.write ("nested_field2", false);
obs.write ("nested_field3", -1234);
});
obs.write ("field2", "value2");
obs.write ("field3", true);
obs.write ("field4", 1234);
});
auto expected = trim (R"(
object_field: {
field1: "value1",
nested_object: {
nested_field1: "nested_value1",
nested_field2: false,
nested_field3: -1234
},
field2: "value2",
field3: true,
field4: 1234
}
)");
ASSERT_EQ (ss.str (), expected);
}
TEST (object_stream, array_writer_basic)
{
std::stringstream ss;
nano::object_stream obs{ ss };
obs.write ("array_field", [] (nano::array_stream & ars) {
ars.write (std::views::iota (0, 3));
});
auto expected = trim (R"(
array_field: [
0,
1,
2
]
)");
ASSERT_EQ (ss.str (), expected);
}
namespace
{
class object_basic
{
public:
nano::uint256_union uint256_union_field{ 0 };
nano::block_hash block_hash{ 0 };
void operator() (nano::object_stream & obs) const
{
obs.write ("uint256_union_field", uint256_union_field);
obs.write ("block_hash", block_hash);
}
};
}
TEST (object_stream, object_basic)
{
std::stringstream ss;
nano::object_stream obs{ ss };
object_basic test_object;
obs.write ("test_object", test_object);
auto expected = trim (R"(
test_object: {
uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000000",
block_hash: "0000000000000000000000000000000000000000000000000000000000000000"
}
)");
ASSERT_EQ (ss.str (), expected);
}
TEST (object_stream, array_writer_objects)
{
std::stringstream ss;
std::vector<object_basic> objects;
objects.push_back ({ .block_hash = 0 });
objects.push_back ({ .block_hash = 1 });
objects.push_back ({ .block_hash = 2 });
nano::object_stream obs{ ss };
obs.write ("array_field", [&objects] (nano::array_stream & ars) {
ars.write (objects);
});
auto expected = trim (R"(
array_field: [
{
uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000000",
block_hash: "0000000000000000000000000000000000000000000000000000000000000000"
},
{
uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000000",
block_hash: "0000000000000000000000000000000000000000000000000000000000000001"
},
{
uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000000",
block_hash: "0000000000000000000000000000000000000000000000000000000000000002"
}
]
)");
ASSERT_EQ (ss.str (), expected);
}
namespace
{
class object_array_basic
{
public:
std::vector<int> values{ 1, 2, 3 };
void operator() (nano::array_stream & ars) const
{
ars.write (values);
}
};
}
TEST (object_stream, object_array_basic)
{
std::stringstream ss;
nano::object_stream obs{ ss };
object_array_basic test_object;
obs.write ("test_object_array", test_object);
auto expected = trim (R"(
test_object_array: [
1,
2,
3
]
)");
ASSERT_EQ (ss.str (), expected);
}
namespace
{
class object_nested
{
public:
nano::uint256_union uint256_union_field{ 0 };
nano::block_hash block_hash{ 0 };
object_basic nested_object;
object_array_basic nested_array_object;
void operator() (nano::object_stream & obs) const
{
obs.write ("uint256_union_field", uint256_union_field);
obs.write ("block_hash", block_hash);
obs.write ("nested_object", nested_object);
obs.write ("nested_array_object", nested_array_object);
}
};
}
TEST (object_stream, object_nested)
{
std::stringstream ss;
nano::object_stream obs{ ss };
object_nested test_object;
obs.write ("test_object", test_object);
auto expected = trim (R"(
test_object: {
uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000000",
block_hash: "0000000000000000000000000000000000000000000000000000000000000000",
nested_object: {
uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000000",
block_hash: "0000000000000000000000000000000000000000000000000000000000000000"
},
nested_array_object: [
1,
2,
3
]
}
)");
ASSERT_EQ (ss.str (), expected);
}
namespace nano
{
using builtin_array_with_pair = std::vector<std::pair<nano::block_hash, int>>;
void stream_as (std::pair<nano::block_hash, int> const & entry, nano::object_stream & obs)
{
auto const & [hash, value] = entry;
obs.write ("hash", hash);
obs.write ("value", value);
}
}
TEST (object_stream, builtin_array)
{
using namespace nano;
std::stringstream ss;
builtin_array_with_pair array;
array.push_back ({ nano::block_hash{ 1 }, 1 });
array.push_back ({ nano::block_hash{ 2 }, 2 });
array.push_back ({ nano::block_hash{ 3 }, 3 });
nano::object_stream obs{ ss };
obs.write_range ("array_field", array);
auto expected = trim (R"(
array_field: [
{
hash: "0000000000000000000000000000000000000000000000000000000000000001",
value: 1
},
{
hash: "0000000000000000000000000000000000000000000000000000000000000002",
value: 2
},
{
hash: "0000000000000000000000000000000000000000000000000000000000000003",
value: 3
}
]
)");
ASSERT_EQ (ss.str (), expected);
}
namespace
{
class streamable_object
{
public:
nano::uint256_union uint256_union_field{ 0 };
nano::block_hash block_hash{ 0 };
void operator() (nano::object_stream & obs) const
{
obs.write ("uint256_union_field", uint256_union_field);
obs.write ("block_hash", block_hash);
}
};
}
TEST (object_stream, ostream_adapter)
{
using namespace nano::object_stream_adapters;
std::stringstream ss1, ss2;
streamable_object test_object;
ss1 << test_object; // Using automatic ostream adapter (in `nano::ostream_operators`)
ss2 << nano::streamed (test_object); // Using explicit ostream adapter
auto expected = trim (R"(
{
uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000000",
block_hash: "0000000000000000000000000000000000000000000000000000000000000000"
}
)");
ASSERT_EQ (ss1.str (), expected);
ASSERT_EQ (ss2.str (), expected);
}
TEST (object_stream, fmt_adapter)
{
streamable_object test_object;
auto str1 = fmt::format ("{}", test_object); // Using automatic fmt adapter
auto str2 = fmt::format ("{}", nano::streamed (test_object)); // Using explicit fmt adapter
auto expected = trim (R"(
{
uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000000",
block_hash: "0000000000000000000000000000000000000000000000000000000000000000"
}
)");
ASSERT_EQ (str1, expected);
ASSERT_EQ (str2, expected);
}
TEST (object_stream, to_string)
{
using namespace nano::object_stream_adapters;
streamable_object test_object;
auto str = to_string (test_object); // Using automatic to_string adapter
auto expected = trim (R"(
{
uint256_union_field: "0000000000000000000000000000000000000000000000000000000000000000",
block_hash: "0000000000000000000000000000000000000000000000000000000000000000"
}
)");
ASSERT_EQ (str, expected);
}
TEST (object_stream, to_json)
{
using namespace nano::object_stream_adapters;
streamable_object test_object;
auto str = to_json (test_object); // Using automatic to_string adapter
auto expected = trim (R"(
{"uint256_union_field":"0000000000000000000000000000000000000000000000000000000000000000","block_hash":"0000000000000000000000000000000000000000000000000000000000000000"}
)");
ASSERT_EQ (str, expected);
}

View file

@ -58,6 +58,9 @@ add_library(
memory.cpp
numbers.hpp
numbers.cpp
object_stream.hpp
object_stream.cpp
object_stream_adapters.hpp
observer_set.hpp
optional_ptr.hpp
processing_queue.hpp

View file

@ -45,6 +45,10 @@ void nano::block_memory_pool_purge ()
nano::purge_shared_ptr_singleton_pool_memory<nano::change_block> ();
}
/*
* block
*/
std::string nano::block::to_json () const
{
std::string result;
@ -183,7 +187,7 @@ nano::account const & nano::block::account () const
nano::qualified_root nano::block::qualified_root () const
{
return nano::qualified_root (root (), previous ());
return { root (), previous () };
}
nano::amount const & nano::block::balance () const
@ -192,6 +196,21 @@ nano::amount const & nano::block::balance () const
return amount;
}
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);
@ -472,6 +491,21 @@ 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),
@ -748,6 +782,21 @@ 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)
@ -999,6 +1048,20 @@ 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),
@ -1319,6 +1382,23 @@ 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;
@ -1428,6 +1508,10 @@ std::shared_ptr<nano::block> nano::deserialize_block (nano::stream & stream_a, n
return result;
}
/*
* receive_block
*/
void nano::receive_block::visit (nano::block_visitor & visitor_a) const
{
visitor_a.receive_block (*this);
@ -1683,6 +1767,20 @@ void nano::receive_hashables::hash (blake2b_state & hash_a) const
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);
}
/*
* block_details
*/
nano::block_details::block_details (nano::epoch const epoch_a, bool const is_send_a, bool const is_receive_a, bool const is_epoch_a) :
epoch (epoch_a), is_send (is_send_a), is_receive (is_receive_a), is_epoch (is_epoch_a)
{
@ -1734,6 +1832,14 @@ bool nano::block_details::deserialize (nano::stream & stream_a)
return result;
}
void nano::block_details::operator() (nano::object_stream & obs) const
{
obs.write ("epoch", epoch);
obs.write ("is_send", is_send);
obs.write ("is_receive", is_receive);
obs.write ("is_epoch", is_epoch);
}
std::string nano::state_subtype (nano::block_details const details_a)
{
debug_assert (details_a.is_epoch + details_a.is_receive + details_a.is_send <= 1);
@ -1755,6 +1861,10 @@ std::string nano::state_subtype (nano::block_details const details_a)
}
}
/*
* block_sideband
*/
nano::block_sideband::block_sideband (nano::account const & account_a, nano::block_hash const & successor_a, nano::amount const & balance_a, uint64_t const height_a, nano::seconds_t const timestamp_a, nano::block_details const & details_a, nano::epoch const source_epoch_a) :
successor (successor_a),
account (account_a),
@ -1866,6 +1976,17 @@ bool nano::block_sideband::deserialize (nano::stream & stream_a, nano::block_typ
return result;
}
void nano::block_sideband::operator() (nano::object_stream & obs) const
{
obs.write ("successor", successor);
obs.write ("account", account);
obs.write ("balance", balance);
obs.write ("height", height);
obs.write ("timestamp", timestamp);
obs.write ("source_epoch", source_epoch);
obs.write ("details", details);
}
std::string_view nano::to_string (nano::block_type type)
{
return magic_enum::enum_name (type);

View file

@ -4,6 +4,7 @@
#include <nano/lib/epoch.hpp>
#include <nano/lib/errors.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/object_stream.hpp>
#include <nano/lib/optional_ptr.hpp>
#include <nano/lib/stream.hpp>
#include <nano/lib/timer.hpp>
@ -19,6 +20,7 @@ namespace nano
{
class block_visitor;
class mutable_block_visitor;
enum class block_type : uint8_t
{
invalid = 0,
@ -55,6 +57,9 @@ public:
private:
uint8_t packed () const;
void unpack (uint8_t);
public: // Logging
void operator() (nano::object_stream &) const;
};
std::string state_subtype (nano::block_details const);
@ -75,7 +80,11 @@ public:
uint64_t timestamp{ 0 };
nano::block_details details;
nano::epoch source_epoch{ nano::epoch::epoch_0 };
public: // Logging
void operator() (nano::object_stream &) const;
};
class block
{
public:
@ -132,6 +141,9 @@ protected:
private:
nano::block_hash generate_hash () const;
public: // Logging
virtual void operator() (nano::object_stream &) const;
};
using block_list_t = std::vector<std::shared_ptr<nano::block>>;
@ -149,6 +161,7 @@ public:
nano::amount balance;
static std::size_t constexpr size = sizeof (previous) + sizeof (destination) + sizeof (balance);
};
class send_block : public nano::block
{
public:
@ -182,7 +195,11 @@ public:
nano::signature signature;
uint64_t work;
static std::size_t constexpr size = nano::send_hashables::size + sizeof (signature) + sizeof (work);
public: // Logging
void operator() (nano::object_stream &) const override;
};
class receive_hashables
{
public:
@ -195,6 +212,7 @@ public:
nano::block_hash source;
static std::size_t constexpr size = sizeof (previous) + sizeof (source);
};
class receive_block : public nano::block
{
public:
@ -227,7 +245,11 @@ public:
nano::signature signature;
uint64_t work;
static std::size_t constexpr size = nano::receive_hashables::size + sizeof (signature) + sizeof (work);
public: // Logging
void operator() (nano::object_stream &) const override;
};
class open_hashables
{
public:
@ -241,6 +263,7 @@ public:
nano::account account;
static std::size_t constexpr size = sizeof (source) + sizeof (representative) + sizeof (account);
};
class open_block : public nano::block
{
public:
@ -276,7 +299,11 @@ public:
nano::signature signature;
uint64_t work;
static std::size_t constexpr size = nano::open_hashables::size + sizeof (signature) + sizeof (work);
public: // Logging
void operator() (nano::object_stream &) const override;
};
class change_hashables
{
public:
@ -289,6 +316,7 @@ public:
nano::account representative;
static std::size_t constexpr size = sizeof (previous) + sizeof (representative);
};
class change_block : public nano::block
{
public:
@ -321,7 +349,11 @@ public:
nano::signature signature;
uint64_t work;
static std::size_t constexpr size = nano::change_hashables::size + sizeof (signature) + sizeof (work);
public: // Logging
void operator() (nano::object_stream &) const override;
};
class state_hashables
{
public:
@ -347,6 +379,7 @@ public:
// Serialized size
static std::size_t constexpr size = sizeof (account) + sizeof (previous) + sizeof (representative) + sizeof (balance) + sizeof (link);
};
class state_block : public nano::block
{
public:
@ -382,7 +415,11 @@ public:
nano::signature signature;
uint64_t work;
static std::size_t constexpr size = nano::state_hashables::size + sizeof (signature) + sizeof (work);
public: // Logging
void operator() (nano::object_stream &) const override;
};
class block_visitor
{
public:

View file

@ -1,5 +1,6 @@
#include <nano/lib/config.hpp>
#include <nano/lib/logging.hpp>
#include <nano/lib/logging_enums.hpp>
#include <nano/lib/utility.hpp>
#include <fmt/chrono.h>
@ -23,6 +24,7 @@ nano::logger & nano::default_logger ()
bool nano::logger::global_initialized{ false };
nano::log_config nano::logger::global_config{};
std::vector<spdlog::sink_ptr> nano::logger::global_sinks{};
nano::object_stream_config nano::logger::global_tracing_config{};
// By default, use only the tag as the logger name, since only one node is running in the process
std::function<std::string (nano::log::logger_id, std::string identifier)> nano::logger::global_name_formatter{ [] (nano::log::logger_id logger_id, std::string identifier) {
@ -43,7 +45,6 @@ namespace
/// Takes a qualified identifier in the form `node_identifier::tag` and splits it into a pair of `identifier` and `tag`
/// It is a limitation of spldlog that we cannot attach additional data to the logger, so we have to encode the node identifier in the logger name
/// @returns <node identifier, tag>
std::pair<std::string_view, std::string_view> split_qualified_identifier (std::string_view qualified_identifier)
{
auto pos = qualified_identifier.find ("::");
@ -185,6 +186,17 @@ void nano::logger::initialize_common (nano::log_config const & config, std::opti
global_sinks.push_back (file_sink);
}
}
// Tracing setup
switch (config.tracing_format)
{
case nano::log::tracing_format::standard:
global_tracing_config = nano::object_stream_config::default_config ();
break;
case nano::log::tracing_format::json:
global_tracing_config = nano::object_stream_config::json_config ();
break;
}
}
void nano::logger::flush ()
@ -453,9 +465,10 @@ nano::log_config nano::load_log_config (nano::log_config fallback, const std::fi
{
try
{
config.default_level = nano::log::parse_level (*env_level);
auto level = nano::log::parse_level (*env_level);
config.default_level = level;
std::cerr << "Using default log level from NANO_LOG environment variable: " << *env_level << std::endl;
std::cerr << "Using default log level from NANO_LOG environment variable: " << to_string (level) << std::endl;
}
catch (std::invalid_argument const & ex)
{
@ -487,7 +500,7 @@ nano::log_config nano::load_log_config (nano::log_config fallback, const std::fi
levels[logger_id] = logger_level;
std::cerr << "Using logger log level from NANO_LOG_LEVELS environment variable: " << name_str << "=" << level_str << std::endl;
std::cerr << "Using logger log level from NANO_LOG_LEVELS environment variable: " << to_string (logger_id) << "=" << to_string (logger_level) << std::endl;
}
catch (std::invalid_argument const & ex)
{
@ -502,6 +515,42 @@ nano::log_config nano::load_log_config (nano::log_config fallback, const std::fi
}
}
auto env_tracing_format = nano::get_env ("NANO_TRACE_FORMAT");
if (env_tracing_format)
{
try
{
auto tracing_format = nano::log::parse_tracing_format (*env_tracing_format);
config.tracing_format = tracing_format;
std::cerr << "Using trace format from NANO_TRACE_FORMAT environment variable: " << to_string (tracing_format) << std::endl;
}
catch (std::invalid_argument const & ex)
{
std::cerr << "Invalid trace format from NANO_TRACE_FORMAT environment variable: " << ex.what () << std::endl;
}
}
auto tracing_configured = [&] () {
if (config.default_level == nano::log::level::trace)
{
return true;
}
for (auto const & [logger_id, level] : config.levels)
{
if (level == nano::log::level::trace)
{
return true;
}
}
return false;
};
if (tracing_configured () && !is_tracing_enabled ())
{
std::cerr << "WARNING: Tracing is not enabled in this build, but log level is set to trace" << std::endl;
}
return config;
}
catch (std::runtime_error const & ex)

View file

@ -1,6 +1,8 @@
#pragma once
#include <nano/lib/logging_enums.hpp>
#include <nano/lib/object_stream.hpp>
#include <nano/lib/object_stream_adapters.hpp>
#include <nano/lib/tomlconfig.hpp>
#include <initializer_list>
@ -8,10 +10,24 @@
#include <shared_mutex>
#include <sstream>
#include <fmt/ostream.h>
#include <spdlog/spdlog.h>
namespace nano::log
{
template <class T>
struct arg
{
std::string_view name;
T const & value;
arg (std::string_view name_a, T const & value_a) :
name{ name_a },
value{ value_a }
{
}
};
using logger_id = std::pair<nano::log::type, nano::log::detail>;
std::string to_string (logger_id);
@ -20,6 +36,15 @@ logger_id parse_logger_id (std::string const &);
namespace nano
{
consteval bool is_tracing_enabled ()
{
#ifdef NANO_TRACING
return true;
#else
return false;
#endif
}
class log_config final
{
public:
@ -53,6 +78,8 @@ public:
console_config console;
file_config file;
nano::log::tracing_format tracing_format{ nano::log::tracing_format::standard };
public: // Predefined defaults
static log_config cli_default ();
static log_config daemon_default ();
@ -76,7 +103,7 @@ public:
logger (logger const &) = delete;
public:
static void initialize (nano::log_config fallback, std::optional<std::filesystem::path> data_path = std::nullopt, std::vector<std::string> const & config_overrides = std::vector<std::string> ());
static void initialize (nano::log_config fallback, std::optional<std::filesystem::path> data_path = std::nullopt, std::vector<std::string> const & config_overrides = {});
static void initialize_for_tests (nano::log_config fallback);
static void flush ();
@ -85,6 +112,7 @@ private:
static nano::log_config global_config;
static std::vector<spdlog::sink_ptr> global_sinks;
static std::function<std::string (nano::log::logger_id, std::string identifier)> global_name_formatter;
static nano::object_stream_config global_tracing_config;
static void initialize_common (nano::log_config const &, std::optional<std::filesystem::path> data_path);
@ -125,6 +153,28 @@ public:
get_logger (type).critical (fmt, std::forward<Args> (args)...);
}
public:
template <typename... Args>
void trace (nano::log::type type, nano::log::detail detail, Args &&... args)
{
if constexpr (is_tracing_enabled ())
{
debug_assert (detail != nano::log::detail::all);
// Include info about precise time of the event
auto now = std::chrono::high_resolution_clock::now ();
auto now_micros = std::chrono::duration_cast<std::chrono::microseconds> (now.time_since_epoch ()).count ();
// TODO: Improve code indentation config
auto logger = get_logger (type, detail);
logger.trace ("{}",
nano::streamed_args (global_tracing_config,
nano::log::arg{ "event", to_string (std::make_pair (type, detail)) },
nano::log::arg{ "time", now_micros },
std::forward<Args> (args)...));
}
}
private:
const std::string identifier;

View file

@ -75,4 +75,39 @@ nano::log::detail nano::log::parse_detail (std::string_view name)
{
throw std::invalid_argument ("Invalid log detail: " + std::string (name));
}
}
std::string_view nano::log::to_string (nano::log::tracing_format format)
{
return magic_enum::enum_name (format);
}
nano::log::tracing_format nano::log::parse_tracing_format (std::string_view name)
{
auto value = magic_enum::enum_cast<nano::log::tracing_format> (name);
if (value.has_value ())
{
return value.value ();
}
else
{
auto all_formats_str = nano::util::join (nano::log::all_tracing_formats (), ", ", [] (auto const & fmt) {
return to_string (fmt);
});
throw std::invalid_argument ("Invalid tracing format: " + std::string (name) + ". Must be one of: " + all_formats_str);
}
}
const std::vector<nano::log::tracing_format> & nano::log::all_tracing_formats ()
{
static std::vector<nano::log::tracing_format> all = [] () {
std::vector<nano::log::tracing_format> result;
for (auto const & fmt : magic_enum::enum_values<nano::log::tracing_format> ())
{
result.push_back (fmt);
}
return result;
}();
return all;
}

View file

@ -24,6 +24,7 @@ enum class type
all = 0, // reserved
generic,
test,
init,
config,
logging,
@ -46,7 +47,9 @@ enum class type
election,
blockprocessor,
network,
network_processed,
channel,
channel_sent,
socket,
socket_server,
tcp,
@ -65,6 +68,10 @@ enum class type
txn_tracker,
gap_cache,
vote_processor,
election_scheduler,
vote_generator,
// bootstrap
bulk_pull_client,
bulk_pull_server,
bulk_pull_account_client,
@ -84,6 +91,8 @@ enum class detail
{
all = 0, // reserved
test,
// node
process_confirmed,
@ -94,6 +103,7 @@ enum class detail
// election
election_confirmed,
election_expired,
broadcast_vote,
// blockprocessor
block_processed,
@ -102,10 +112,17 @@ enum class detail
vote_processed,
// network
message_received,
message_processed,
message_sent,
message_dropped,
// election_scheduler
block_activated,
// vote_generator
candidate_processed,
should_vote,
// bulk pull/push
pulled_block,
sending_block,
@ -114,6 +131,24 @@ enum class detail
requesting_account_or_head,
requesting_pending,
// message types
not_a_type,
invalid,
keepalive,
publish,
republish_vote,
confirm_req,
confirm_ack,
node_id_handshake,
telemetry_req,
telemetry_ack,
asc_pull_req,
asc_pull_ack,
bulk_pull,
bulk_push,
frontier_req,
bulk_pull_account,
_last // Must be the last enum
};
@ -127,6 +162,12 @@ enum class category
_last // Must be the last enum
};
enum class tracing_format
{
standard,
json,
};
}
namespace nano::log
@ -146,6 +187,10 @@ nano::log::detail parse_detail (std::string_view);
std::vector<nano::log::level> const & all_levels ();
std::vector<nano::log::type> const & all_types ();
std::string_view to_string (nano::log::tracing_format);
nano::log::tracing_format parse_tracing_format (std::string_view);
std::vector<nano::log::tracing_format> const & all_tracing_formats ();
}
// Ensure that the enum_range is large enough to hold all values (including future ones)

View file

@ -0,0 +1,28 @@
#include <nano/lib/object_stream.hpp>
nano::object_stream_config const & nano::object_stream_config::default_config ()
{
static object_stream_config const config{};
return config;
}
nano::object_stream_config const & nano::object_stream_config::json_config ()
{
static object_stream_config const config{
.field_name_begin = "\"",
.field_name_end = "\"",
.field_assignment = ":",
.field_separator = ",",
.object_begin = "{",
.object_end = "}",
.array_begin = "[",
.array_end = "]",
.array_element_begin = "",
.array_element_end = "",
.array_element_separator = ",",
.indent = "",
.newline = "",
.precision = 4,
};
return config;
}

555
nano/lib/object_stream.hpp Normal file
View file

@ -0,0 +1,555 @@
#pragma once
#include <cstdint>
#include <iomanip>
#include <memory>
#include <ostream>
#include <ranges>
#include <string_view>
#include <type_traits>
#include <fmt/ostream.h>
#include <magic_enum_iostream.hpp>
namespace nano
{
struct object_stream_config
{
std::string field_name_begin{ "" };
std::string field_name_end{ "" };
std::string field_assignment{ ": " };
std::string field_separator{ "," };
std::string object_begin{ "{" };
std::string object_end{ "}" };
std::string array_begin{ "[" };
std::string array_end{ "]" };
std::string array_element_begin{ "" };
std::string array_element_end{ "" };
std::string array_element_separator{ "," };
std::string string_begin{ "\"" };
std::string string_end{ "\"" };
std::string true_value{ "true" };
std::string false_value{ "false" };
std::string null_value{ "null" };
std::string indent{ "\t" };
std::string newline{ "\n" };
/** Number of decimal places to show for `float` and `double` */
int precision{ 2 };
static object_stream_config const & default_config ();
static object_stream_config const & json_config ();
};
class object_stream_context
{
public:
object_stream_config const & config;
explicit object_stream_context (std::ostream & os, object_stream_config const & config = object_stream_config::default_config ()) :
os{ os },
config{ config }
{
}
// Bump indent level when nesting objects
object_stream_context (object_stream_context const & other) :
os{ other.os },
config{ other.config },
indent_level{ other.indent_level + 1 }
{
}
private:
std::ostream & os;
int indent_level{ 0 };
bool needs_newline{ false };
public: // Keep these defined in the header for inlining
std::ostream & begin_stream ()
{
return os;
}
void begin_field (std::string_view name, bool first)
{
if (!first)
{
os << config.field_separator;
}
if (std::exchange (needs_newline, false))
{
os << config.newline;
}
indent ();
os << config.field_name_begin << name << config.field_name_end << config.field_assignment;
}
void end_field ()
{
needs_newline = true;
}
void begin_object ()
{
os << config.object_begin;
os << config.newline;
}
void end_object ()
{
os << config.newline;
indent ();
os << config.object_end;
needs_newline = true;
}
void begin_array ()
{
os << config.array_begin;
os << config.newline;
}
void end_array ()
{
os << config.newline;
indent ();
os << config.array_end;
needs_newline = true;
}
void begin_array_element (bool first)
{
if (!first)
{
os << config.array_element_separator;
}
if (std::exchange (needs_newline, false))
{
os << config.newline;
}
indent ();
os << config.array_element_begin;
}
void end_array_element ()
{
os << config.array_element_end;
needs_newline = true;
}
void begin_string ()
{
os << config.string_begin;
}
void end_string ()
{
os << config.string_end;
}
private:
void indent ()
{
if (!config.indent.empty ())
{
for (int i = 0; i < indent_level; ++i)
{
os << config.indent;
}
}
}
};
class object_stream;
class array_stream;
/*
* Concepts used for choosing the correct writing function
*/
template <typename T>
concept object_streamable = requires (T const & obj, object_stream & obs) {
{
stream_as (obj, obs)
};
};
template <typename T>
concept array_streamable = requires (T const & obj, array_stream & ars) {
{
stream_as (obj, ars)
};
};
template <typename T>
concept object_or_array_streamable = object_streamable<T> || array_streamable<T>;
class object_stream_base
{
public:
explicit object_stream_base (object_stream_context const & ctx) :
ctx{ ctx }
{
}
explicit object_stream_base (std::ostream & os, object_stream_config const & config = object_stream_config::default_config ()) :
ctx{ os, config }
{
}
protected:
object_stream_context ctx;
};
/**
* Used to serialize an object.
* Outputs: `field1: value1, field2: value2, ...` (without enclosing `{}`)
*/
class object_stream : private object_stream_base
{
public:
// Inherit default constructors
using object_stream_base::object_stream_base;
object_stream (object_stream const &) = delete; // Disallow copying
public:
template <class Value>
void write (std::string_view name, Value const & value)
{
ctx.begin_field (name, std::exchange (first_field, false));
stream_as_value (value, ctx);
ctx.end_field ();
}
// Handle `.write_range ("name", container)`
template <class Container>
inline void write_range (std::string_view name, Container const & container);
// Handle `.write_range ("name", container, [] (auto const & entry) { ... })`
template <class Container, class Transform>
requires (std::is_invocable_v<Transform, typename Container::value_type>)
void write_range (std::string_view name, Container const & container, Transform transform)
{
write_range (name, std::views::transform (container, transform));
}
// Handle `.write_range ("name", container, [] (auto const & entry, nano::object_stream &) { ... })`
template <class Container, class Writer>
requires (std::is_invocable_v<Writer, typename Container::value_type, object_stream &>)
void write_range (std::string_view name, Container const & container, Writer writer)
{
write_range (name, container, [&writer] (auto const & el) {
return [&writer, &el] (object_stream & obs) {
writer (el, obs);
};
});
}
// Handle `.write_range ("name", container, [] (auto const & entry, nano::array_stream &) { ... })`
template <class Container, class Writer>
requires (std::is_invocable_v<Writer, typename Container::value_type, array_stream &>)
void write_range (std::string_view name, Container const & container, Writer writer)
{
write_range (name, container, [&writer] (auto const & el) {
return [&writer, &el] (array_stream & obs) {
writer (el, obs);
};
});
}
private:
bool first_field{ true };
};
/**
* Used to serialize an array of objects.
* Outputs: `[value1, value2, ...]`
*/
class array_stream : private object_stream_base
{
public:
// Inherit default constructors
using object_stream_base::object_stream_base;
array_stream (array_stream const &) = delete; // Disallow copying
private:
template <class Value>
void write_single (Value const & value)
{
ctx.begin_array_element (std::exchange (first_element, false));
stream_as_value (value, ctx);
ctx.end_array_element ();
}
public:
// Handle `.write (container)`
template <class Container>
void write (Container const & container)
{
for (auto const & el : container)
{
write_single (el);
};
}
// Handle `.write (container, [] (auto const & entry) { ... })`
template <class Container, class Transform>
requires (std::is_invocable_v<Transform, typename Container::value_type>)
void write (Container const & container, Transform transform)
{
write (std::views::transform (container, transform));
}
// Handle `.write (container, [] (auto const & entry, nano::object_stream &) { ... })`
template <class Container, class Writer>
requires (std::is_invocable_v<Writer, typename Container::value_type, object_stream &>)
void write (Container const & container, Writer writer)
{
write (container, [&writer] (auto const & el) {
return [&writer, &el] (object_stream & obs) {
writer (el, obs);
};
});
}
// Handle `.write_range (container, [] (auto const & entry, nano::array_stream &) { ... })`
template <class Container, class Writer>
requires (std::is_invocable_v<Writer, typename Container::value_type, array_stream &>)
void write (Container const & container, Writer writer)
{
write (container, [&writer] (auto const & el) {
return [&writer, &el] (array_stream & obs) {
writer (el, obs);
};
});
}
private:
bool first_element{ true };
};
/**
* Used for human readable object serialization. Should be used to serialize a single object.
* Outputs: `{ field1: value1, field2: value2, ... }`
*/
class root_object_stream : private object_stream_base
{
public:
// Inherit default constructors
using object_stream_base::object_stream_base;
public:
template <class Value>
void write (Value const & value)
{
stream_as_value (value, ctx);
}
// Handle `.write_range (container)`
template <class Container>
inline void write_range (Container const & container);
// Handle `.write_range (container, [] (auto const & entry) { ... })`
template <class Container, class Transform>
requires (std::is_invocable_v<Transform, typename Container::value_type>)
void write_range (Container const & container, Transform transform)
{
write_range (std::views::transform (container, transform));
}
// Handle `.write_range (container, [] (auto const & entry, nano::object_stream &) { ... })`
template <class Container, class Writer>
requires (std::is_invocable_v<Writer, typename Container::value_type, object_stream &>)
void write_range (Container const & container, Writer writer)
{
write_range (container, [&writer] (auto const & el) {
return [&writer, &el] (object_stream & obs) {
writer (el, obs);
};
});
}
// Handle `.write_range (container, [] (auto const & entry, nano::array_stream &) { ... })`
template <class Container, class Writer>
requires (std::is_invocable_v<Writer, typename Container::value_type, array_stream &>)
void write_range (Container const & container, Writer writer)
{
write_range (container, [&writer] (auto const & el) {
return [&writer, &el] (array_stream & obs) {
writer (el, obs);
};
});
}
};
/*
* Implementation for `write_range` functions
*/
template <class Container>
inline void nano::object_stream::write_range (std::string_view name, Container const & container)
{
write (name, [&container] (array_stream & ars) {
ars.write (container);
});
}
template <class Container>
inline void nano::root_object_stream::write_range (Container const & container)
{
write ([&container] (array_stream & ars) {
ars.write (container);
});
}
/*
* Writers
*/
template <class Value>
inline void stream_as_value (Value const & value, object_stream_context & ctx)
{
// Automatically support printing all enums
using magic_enum::iostream_operators::operator<<;
ctx.begin_string ();
ctx.begin_stream () << value; // Write using type specific ostream operator
ctx.end_string ();
}
template <object_streamable Value>
inline void stream_as_value (Value const & value, object_stream_context & ctx)
{
ctx.begin_object ();
// Write as object
nano::object_stream obs{ ctx };
stream_as (value, obs);
ctx.end_object ();
}
template <array_streamable Value>
inline void stream_as_value (Value const & value, object_stream_context & ctx)
{
ctx.begin_array ();
// Write as array
nano::array_stream ars{ ctx };
stream_as (value, ars);
ctx.end_array ();
}
/*
* Adapters for types implementing convenience `obj(object_stream &)` & `obj(array_stream &)` functions
*/
template <typename T>
concept simple_object_streamable = requires (T const & obj, object_stream & obs) {
{
obj (obs)
};
};
template <typename T>
concept simple_array_streamable = requires (T const & obj, array_stream & ars) {
{
obj (ars)
};
};
template <simple_object_streamable Value>
inline void stream_as (Value const & value, object_stream & obs)
{
value (obs);
}
template <simple_array_streamable Value>
inline void stream_as (Value const & value, array_stream & ars)
{
value (ars);
}
}
/*
* Specializations for primitive types
*/
namespace nano
{
template <class Value>
requires (std::is_integral_v<Value> && sizeof (Value) > 1) // Exclude bool, char, etc.
inline void stream_as_value (const Value & value, object_stream_context & ctx)
{
ctx.begin_stream () << value;
}
template <class Value>
requires (std::is_floating_point_v<Value>)
inline void stream_as_value (const Value & value, object_stream_context & ctx)
{
ctx.begin_stream () << std::fixed << std::setprecision (ctx.config.precision) << value;
}
inline void stream_as_value (bool const & value, object_stream_context & ctx)
{
ctx.begin_stream () << (value ? ctx.config.true_value : ctx.config.false_value);
}
inline void stream_as_value (const int8_t & value, object_stream_context & ctx)
{
ctx.begin_stream () << static_cast<int32_t> (value); // Avoid printing as char
}
inline void stream_as_value (const uint8_t & value, object_stream_context & ctx)
{
ctx.begin_stream () << static_cast<uint32_t> (value); // Avoid printing as char
}
template <class Opt>
inline void stream_as_optional (const Opt & opt, object_stream_context & ctx)
{
if (opt)
{
stream_as_value (*opt, ctx);
}
else
{
ctx.begin_stream () << ctx.config.null_value;
}
}
template <class Value>
inline void stream_as_value (std::shared_ptr<Value> const & value, object_stream_context & ctx)
{
stream_as_optional (value, ctx);
}
template <class Value>
inline void stream_as_value (std::unique_ptr<Value> const & value, object_stream_context & ctx)
{
stream_as_optional (value, ctx);
}
template <class Value>
inline void stream_as_value (std::weak_ptr<Value> const & value, object_stream_context & ctx)
{
stream_as_optional (value.lock (), ctx);
}
template <class Value>
inline void stream_as_value (std::optional<Value> const & value, object_stream_context & ctx)
{
stream_as_optional (value, ctx);
}
}

View file

@ -0,0 +1,127 @@
#pragma once
#include <nano/lib/object_stream.hpp>
#include <ostream>
#include <sstream>
#include <fmt/ostream.h>
namespace nano
{
template <class Streamable>
struct object_stream_formatter
{
nano::object_stream_config const & config;
Streamable const & value;
explicit object_stream_formatter (Streamable const & value, nano::object_stream_config const & config) :
config{ config },
value{ value }
{
}
friend std::ostream & operator<< (std::ostream & os, object_stream_formatter<Streamable> const & self)
{
nano::root_object_stream obs{ os, self.config };
obs.write (self.value);
return os;
}
// Needed for fmt formatting, uses the ostream operator under the hood
friend auto format_as (object_stream_formatter<Streamable> const & val)
{
return fmt::streamed (val);
}
};
template <class Streamable>
auto streamed (Streamable const & value)
{
return object_stream_formatter{ value, nano::object_stream_config::default_config () };
}
template <class Streamable>
auto streamed_as_json (Streamable const & value)
{
return object_stream_formatter{ value, nano::object_stream_config::json_config () };
}
/**
* Wraps {name,value} args and provides `<<(std::ostream &, ...)` and fmt format operator that writes the arguments to the stream in a lazy manner.
*/
template <class... Args>
struct object_stream_args_formatter
{
nano::object_stream_config const & config;
std::tuple<Args...> args;
explicit object_stream_args_formatter (nano::object_stream_config const & config, Args &&... args) :
config{ config },
args{ std::forward<Args> (args)... }
{
}
friend std::ostream & operator<< (std::ostream & os, object_stream_args_formatter<Args...> const & self)
{
nano::object_stream obs{ os, self.config };
std::apply ([&obs] (auto &&... args) {
((obs.write (args.name, args.value)), ...);
},
self.args);
return os;
}
// Needed for fmt formatting, uses the ostream operator under the hood
friend auto format_as (object_stream_args_formatter<Args...> const & val)
{
return fmt::streamed (val);
}
};
template <class... Args>
auto streamed_args (nano::object_stream_config const & config, Args &&... args)
{
return object_stream_args_formatter<Args...>{ config, std::forward<Args> (args)... };
}
}
/*
* Adapter that allows for printing using '<<' operator for all classes that implement object streaming
*/
namespace nano::object_stream_adapters
{
template <nano::object_or_array_streamable Value>
std::ostream & operator<< (std::ostream & os, Value const & value)
{
return os << nano::streamed (value);
}
template <nano::object_or_array_streamable Value>
std::string to_string (Value const & value)
{
std::stringstream ss;
ss << nano::streamed (value);
return ss.str ();
}
template <nano::object_or_array_streamable Value>
std::string to_json (Value const & value)
{
std::stringstream ss;
ss << nano::streamed_as_json (value);
return ss.str ();
}
}
/*
* Adapter that allows for printing using fmt library for all classes that implement object streaming
*/
template <nano::object_or_array_streamable Streamable>
struct fmt::formatter<Streamable> : fmt::ostream_formatter
{
auto format (Streamable const & value, format_context & ctx)
{
return fmt::ostream_formatter::format (nano::streamed (value), ctx);
}
};

View file

@ -152,6 +152,14 @@ enum class detail : uint8_t
vote_processed,
vote_cached,
election_block_conflict,
election_restart,
election_not_confirmed,
election_hinted_overflow,
election_hinted_confirmed,
election_hinted_drop,
broadcast_vote,
broadcast_vote_normal,
broadcast_vote_final,
generate_vote,
generate_vote_normal,
generate_vote_final,

View file

@ -322,9 +322,10 @@ void nano::active_transactions::cleanup_election (nano::unique_lock<nano::mutex>
roots.get<tag_root> ().erase (roots.get<tag_root> ().find (election->qualified_root));
lock_a.unlock ();
node.stats.inc (completion_type (*election), to_stat_detail (election->behavior ()));
node.logger.trace (nano::log::type::active_transactions, nano::log::detail::active_stopped, nano::log::arg{ "election", election });
lock_a.unlock ();
vacancy_update ();
@ -421,7 +422,9 @@ nano::election_insertion_result nano::active_transactions::insert (std::shared_p
nano::election_insertion_result result;
if (stopped)
{
return result;
}
auto const root = block_a->qualified_root ();
auto const hash = block_a->hash ();
@ -442,6 +445,11 @@ nano::election_insertion_result nano::active_transactions::insert (std::shared_p
// Keep track of election count by election type
debug_assert (count_by_behavior[result.election->behavior ()] >= 0);
count_by_behavior[result.election->behavior ()]++;
node.stats.inc (nano::stat::type::active_started, to_stat_detail (election_behavior_a));
node.logger.trace (nano::log::type::active_transactions, nano::log::detail::active_started,
nano::log::arg{ "behavior", election_behavior_a },
nano::log::arg{ "election", result.election });
}
else
{
@ -457,11 +465,13 @@ nano::election_insertion_result nano::active_transactions::insert (std::shared_p
if (result.inserted)
{
release_assert (result.election);
if (auto const cache = node.vote_cache.find (hash); cache)
{
cache->fill (result.election);
}
node.stats.inc (nano::stat::type::active_started, to_stat_detail (election_behavior_a));
node.observers.active_started.notify (hash);
vacancy_update ();
}

View file

@ -255,6 +255,10 @@ nano::process_return nano::block_processor::process_one (store::write_transactio
result = node.ledger.process (transaction_a, *block);
node.stats.inc (nano::stat::type::blockprocessor, to_stat_detail (result.code));
node.logger.trace (nano::log::type::blockprocessor, nano::log::detail::block_processed,
nano::log::arg{ "result", result.code },
nano::log::arg{ "forced", forced_a },
nano::log::arg{ "block", block });
switch (result.code)
{

View file

@ -80,6 +80,10 @@ void nano::bulk_pull_client::request ()
req.count = pull.count;
req.set_count_present (pull.count != 0);
node->logger.trace (nano::log::type::bulk_pull_client, nano::log::detail::requesting_account_or_head,
nano::log::arg{ "account_or_head", pull.account_or_head },
nano::log::arg{ "channel", connection->channel });
if (attempt->should_log ())
{
node->logger.debug (nano::log::type::bulk_pull_client, "Accounts in pull queue: {}", attempt->pulling.load ());
@ -166,6 +170,8 @@ void nano::bulk_pull_client::received_block (boost::system::error_code ec, std::
}
auto hash = block->hash ();
node->logger.trace (nano::log::type::bulk_pull_client, nano::log::detail::pulled_block, nano::log::arg{ "block", block });
// Is block expected?
bool block_expected (false);
// Unconfirmed head is used only for lazy destinations if legacy bootstrap is not available, see nano::bootstrap_attempt::lazy_destinations_increment (...)
@ -232,6 +238,10 @@ void nano::bulk_pull_account_client::request ()
req.minimum_amount = node->config.receive_minimum;
req.flags = nano::bulk_pull_account_flags::pending_hash_and_amount;
node->logger.trace (nano::log::type::bulk_pull_account_client, nano::log::detail::requesting_pending,
nano::log::arg{ "account", req.account.to_account () }, // TODO: Convert to lazy eval
nano::log::arg{ "connection", connection->channel });
if (attempt->should_log ())
{
node->logger.debug (nano::log::type::bulk_pull_account_client, "Accounts in pull queue: {}", attempt->wallet_size ());
@ -413,6 +423,10 @@ void nano::bulk_pull_server::send_next ()
auto block = get_next ();
if (block != nullptr)
{
node->logger.trace (nano::log::type::bulk_pull_server, nano::log::detail::sending_block,
nano::log::arg{ "block", block },
nano::log::arg{ "socket", connection->socket });
std::vector<uint8_t> send_buffer;
{
nano::vectorstream stream (send_buffer);
@ -681,11 +695,17 @@ void nano::bulk_pull_account_server::send_next_block ()
std::vector<uint8_t> send_buffer;
if (pending_address_only)
{
node->logger.trace (nano::log::type::bulk_pull_account_server, nano::log::detail::sending_pending,
nano::log::arg{ "pending", block_info->source });
nano::vectorstream output_stream (send_buffer);
write (output_stream, block_info->source.bytes);
}
else
{
node->logger.trace (nano::log::type::bulk_pull_account_server, nano::log::detail::sending_block,
nano::log::arg{ "block", block_info_key->hash });
nano::vectorstream output_stream (send_buffer);
write (output_stream, block_info_key->hash.bytes);
write (output_stream, block_info->amount.bytes);

View file

@ -275,6 +275,11 @@ void nano::frontier_req_server::send_next ()
}
if (!current.is_zero () && count < request->count)
{
node->logger.trace (nano::log::type::frontier_req_server, nano::log::detail::sending_frontier,
nano::log::arg{ "account", current.to_account () }, // TODO: Convert to lazy eval
nano::log::arg{ "frontier", frontier },
nano::log::arg{ "socket", connection->socket });
std::vector<uint8_t> send_buffer;
{
nano::vectorstream stream (send_buffer);

View file

@ -20,6 +20,10 @@ nano::election_vote_result::election_vote_result (bool replay_a, bool processed_
processed = processed_a;
}
/*
* election
*/
nano::election::election (nano::node & node_a, std::shared_ptr<nano::block> const & block_a, std::function<void (std::shared_ptr<nano::block> const &)> const & confirmation_action_a, std::function<void (nano::account const &)> const & live_vote_action_a, nano::election_behavior election_behavior_a) :
confirmation_action (confirmation_action_a),
live_vote_action (live_vote_action_a),
@ -56,6 +60,11 @@ void nano::election::confirm_once (nano::unique_lock<nano::mutex> & lock_a, nano
node.active.recently_confirmed.put (qualified_root, status_l.winner->hash ());
node.logger.trace (nano::log::type::election, nano::log::detail::election_confirmed,
nano::log::arg{ "id", id },
nano::log::arg{ "qualified_root", qualified_root },
nano::log::arg{ "status", current_status_locked () });
lock_a.unlock ();
node.background ([node_l = node.shared (), status_l, confirmation_action_l = confirmation_action] () {
@ -251,6 +260,11 @@ bool nano::election::transition_time (nano::confirmation_solicitor & solicitor_a
// state_change returning true would indicate it
if (!state_change (state_m, nano::election::state_t::expired_unconfirmed))
{
node.logger.trace (nano::log::type::election, nano::log::detail::election_expired,
nano::log::arg{ "id", id },
nano::log::arg{ "qualified_root", qualified_root },
nano::log::arg{ "status", current_status_locked () });
result = true; // Return true to indicate this election should be cleaned up
status.type = nano::election_status_type::stopped;
}
@ -458,6 +472,7 @@ nano::election_vote_result nano::election::vote (nano::account const & rep, uint
return nano::election_vote_result (false, false);
}
}
last_votes[rep] = { std::chrono::steady_clock::now (), timestamp_a, block_hash_a };
if (vote_source_a == vote_source::live)
{
@ -465,6 +480,15 @@ nano::election_vote_result nano::election::vote (nano::account const & rep, uint
}
node.stats.inc (nano::stat::type::election, vote_source_a == vote_source::live ? nano::stat::detail::vote_new : nano::stat::detail::vote_cached);
node.logger.trace (nano::log::type::election, nano::log::detail::vote_processed,
nano::log::arg{ "id", id },
nano::log::arg{ "qualified_root", qualified_root },
nano::log::arg{ "account", rep },
nano::log::arg{ "hash", block_hash_a },
nano::log::arg{ "final", nano::vote::is_final_timestamp (timestamp_a) },
nano::log::arg{ "timestamp", timestamp_a },
nano::log::arg{ "vote_source", vote_source_a },
nano::log::arg{ "weight", weight });
if (!confirmed_locked ())
{
@ -518,11 +542,18 @@ bool nano::election::publish (std::shared_ptr<nano::block> const & block_a)
nano::election_extended_status nano::election::current_status () const
{
nano::lock_guard<nano::mutex> guard{ mutex };
return current_status_locked ();
}
nano::election_extended_status nano::election::current_status_locked () const
{
debug_assert (!mutex.try_lock ());
nano::election_status status_l = status;
status_l.confirmation_request_count = confirmation_request_count;
status_l.block_count = nano::narrow_cast<decltype (status_l.block_count)> (last_blocks.size ());
status_l.voter_count = nano::narrow_cast<decltype (status_l.voter_count)> (last_votes.size ());
return nano::election_extended_status{ status_l, last_votes, tally_impl () };
return nano::election_extended_status{ status_l, last_votes, last_blocks, tally_impl () };
}
std::shared_ptr<nano::block> nano::election::winner () const
@ -540,18 +571,31 @@ void nano::election::broadcast_vote_locked (nano::unique_lock<nano::mutex> & loc
return;
}
last_vote = std::chrono::steady_clock::now ();
if (node.config.enable_voting && node.wallets.reps ().voting > 0)
{
node.stats.inc (nano::stat::type::election, nano::stat::detail::generate_vote);
node.stats.inc (nano::stat::type::election, nano::stat::detail::broadcast_vote);
if (confirmed_locked () || have_quorum (tally_impl ()))
{
node.stats.inc (nano::stat::type::election, nano::stat::detail::generate_vote_final);
node.stats.inc (nano::stat::type::election, nano::stat::detail::broadcast_vote_final);
node.logger.trace (nano::log::type::election, nano::log::detail::broadcast_vote,
nano::log::arg{ "id", id },
nano::log::arg{ "qualified_root", qualified_root },
nano::log::arg{ "winner", status.winner },
nano::log::arg{ "type", "final" });
node.final_generator.add (root, status.winner->hash ()); // Broadcasts vote to the network
}
else
{
node.stats.inc (nano::stat::type::election, nano::stat::detail::generate_vote_normal);
node.stats.inc (nano::stat::type::election, nano::stat::detail::broadcast_vote_normal);
node.logger.trace (nano::log::type::election, nano::log::detail::broadcast_vote,
nano::log::arg{ "id", id },
nano::log::arg{ "qualified_root", qualified_root },
nano::log::arg{ "winner", status.winner },
nano::log::arg{ "type", "normal" });
node.generator.add (root, status.winner->hash ()); // Broadcasts vote to the network
}
}
@ -701,3 +745,44 @@ nano::election_behavior nano::election::behavior () const
{
return behavior_m;
}
// TODO: Remove the need for .to_string () calls
void nano::election::operator() (nano::object_stream & obs) const
{
obs.write ("id", id);
obs.write ("qualified_root", qualified_root.to_string ());
obs.write ("behaviour", behavior_m);
obs.write ("height", height);
obs.write ("status", current_status ());
}
void nano::election_extended_status::operator() (nano::object_stream & obs) const
{
obs.write ("winner", status.winner->hash ().to_string ());
obs.write ("tally_amount", status.tally.to_string_dec ());
obs.write ("final_tally_amount", status.final_tally.to_string_dec ());
obs.write ("confirmation_request_count", status.confirmation_request_count);
obs.write ("block_count", status.block_count);
obs.write ("voter_count", status.voter_count);
obs.write ("type", status.type);
obs.write_range ("votes", votes, [] (auto const & entry, nano::object_stream & obs) {
auto & [account, info] = entry;
obs.write ("account", account.to_account ());
obs.write ("hash", info.hash.to_string ());
obs.write ("final", nano::vote::is_final_timestamp (info.timestamp));
obs.write ("timestamp", info.timestamp);
obs.write ("time", info.time.time_since_epoch ().count ());
});
obs.write_range ("blocks", blocks, [] (auto const & entry) {
auto [hash, block] = entry;
return block;
});
obs.write_range ("tally", tally, [] (auto const & entry, nano::object_stream & obs) {
auto & [amount, block] = entry;
obs.write ("hash", block->hash ().to_string ());
obs.write ("amount", amount);
});
}

View file

@ -1,5 +1,6 @@
#pragma once
#include <nano/lib/id_dispenser.hpp>
#include <nano/lib/logging.hpp>
#include <nano/secure/common.hpp>
#include <nano/secure/ledger.hpp>
@ -70,7 +71,10 @@ struct election_extended_status final
{
nano::election_status status;
std::unordered_map<nano::account, nano::vote_info> votes;
std::unordered_map<nano::block_hash, std::shared_ptr<nano::block>> blocks;
nano::tally_t tally;
void operator() (nano::object_stream &) const;
};
class election final : public std::enable_shared_from_this<nano::election>
@ -169,6 +173,7 @@ public: // Information
private:
nano::tally_t tally_impl () const;
bool confirmed_locked () const;
nano::election_extended_status current_status_locked () const;
// lock_a does not own the mutex on return
void confirm_once (nano::unique_lock<nano::mutex> & lock_a, nano::election_status_type = nano::election_status_type::active_confirmed_quorum);
void broadcast_block (nano::confirmation_solicitor &);
@ -203,6 +208,9 @@ private:
mutable nano::mutex mutex;
public: // Logging
void operator() (nano::object_stream &) const;
private: // Constants
static std::size_t constexpr max_blocks{ 10 };

View file

@ -19,6 +19,8 @@
#include <chrono>
#include <cstdint>
#include <memory>
#include <optional>
#include <ranges>
#include <sstream>
#include <stdexcept>
#include <string>
@ -82,25 +84,6 @@ bool nano::message_header::deserialize (nano::stream & stream_a)
return error;
}
std::string nano::message_header::to_string () const
{
// Cast to uint16_t to get integer value since uint8_t is treated as an unsigned char in string formatting.
uint16_t type_l = static_cast<uint16_t> (type);
uint16_t version_max_l = static_cast<uint16_t> (version_max);
uint16_t version_using_l = static_cast<uint16_t> (version_using);
uint16_t version_min_l = static_cast<uint16_t> (version_min);
auto type_text = nano::to_string (type);
std::stringstream stream;
stream << boost::format ("NetID: %1%(%2%), ") % nano::to_string_hex (static_cast<uint16_t> (network)) % nano::to_string (network);
stream << boost::format ("VerMaxUsingMin: %1%/%2%/%3%, ") % version_max_l % version_using_l % version_min_l;
stream << boost::format ("MsgType: %1%(%2%), ") % type_l % type_text;
stream << boost::format ("Extensions: %1%") % nano::to_string_hex (static_cast<uint16_t> (extensions.to_ulong ()));
return stream.str ();
}
nano::block_type nano::message_header::block_type () const
{
return static_cast<nano::block_type> (((extensions & block_type_mask) >> 8).to_ullong ());
@ -316,6 +299,17 @@ bool nano::message_header::is_valid_message_type () const
}
}
void nano::message_header::operator() (nano::object_stream & obs) const
{
obs.write ("type", type);
obs.write ("network", to_string (network));
obs.write ("network_raw", static_cast<uint16_t> (network));
obs.write ("version", static_cast<uint16_t> (version_using));
obs.write ("version_min", static_cast<uint16_t> (version_min));
obs.write ("version_max", static_cast<uint16_t> (version_max));
obs.write ("extensions", static_cast<uint16_t> (extensions.to_ulong ()));
}
/*
* message
*/
@ -348,6 +342,11 @@ nano::message_type nano::message::type () const
return header.type;
}
void nano::message::operator() (nano::object_stream & obs) const
{
obs.write ("header", header);
}
/*
* keepalive
*/
@ -413,19 +412,11 @@ bool nano::keepalive::operator== (nano::keepalive const & other_a) const
return peers == other_a.peers;
}
std::string nano::keepalive::to_string () const
void nano::keepalive::operator() (nano::object_stream & obs) const
{
std::stringstream stream;
nano::message::operator() (obs); // Write common data
stream << header.to_string ();
for (auto peer = peers.begin (); peer != peers.end (); ++peer)
{
stream << "\n"
<< peer->address ().to_string () + ":" + std::to_string (peer->port ());
}
return stream.str ();
obs.write_range ("peers", peers);
}
/*
@ -474,9 +465,11 @@ bool nano::publish::operator== (nano::publish const & other_a) const
return *block == *other_a.block;
}
std::string nano::publish::to_string () const
void nano::publish::operator() (nano::object_stream & obs) const
{
return header.to_string () + "\n" + block->to_json ();
nano::message::operator() (obs); // Write common data
obs.write ("block", block);
}
/*
@ -579,19 +572,6 @@ bool nano::confirm_req::operator== (nano::confirm_req const & other_a) const
return equal;
}
std::string nano::confirm_req::roots_string () const
{
std::string result;
for (auto & root_hash : roots_hashes)
{
result += root_hash.first.to_string ();
result += ":";
result += root_hash.second.to_string ();
result += ", ";
}
return result;
}
uint8_t nano::confirm_req::hash_count (const nano::message_header & header)
{
if (header.confirm_is_v2 ())
@ -610,16 +590,16 @@ std::size_t nano::confirm_req::size (nano::message_header const & header)
return count * (sizeof (decltype (roots_hashes)::value_type::first) + sizeof (decltype (roots_hashes)::value_type::second));
}
std::string nano::confirm_req::to_string () const
void nano::confirm_req::operator() (nano::object_stream & obs) const
{
std::string s = header.to_string ();
nano::message::operator() (obs); // Write common data
for (auto && roots_hash : roots_hashes)
{
s += "\n" + roots_hash.first.to_string () + ":" + roots_hash.second.to_string ();
}
return s;
// Write roots as: [ { root: ##, hash: ## } ,...]
obs.write_range ("roots", roots_hashes, [] (auto const & root_hash, nano::object_stream & obs) {
auto [root, hash] = root_hash;
obs.write ("root", root);
obs.write ("hash", hash);
});
}
/*
@ -691,9 +671,11 @@ std::size_t nano::confirm_ack::size (const nano::message_header & header)
return nano::vote::size (count);
}
std::string nano::confirm_ack::to_string () const
void nano::confirm_ack::operator() (nano::object_stream & obs) const
{
return header.to_string () + "\n" + vote->to_json ();
nano::message::operator() (obs); // Write common data
obs.write ("vote", vote);
}
/*
@ -750,13 +732,13 @@ bool nano::frontier_req::operator== (nano::frontier_req const & other_a) const
return start == other_a.start && age == other_a.age && count == other_a.count;
}
std::string nano::frontier_req::to_string () const
void nano::frontier_req::operator() (nano::object_stream & obs) const
{
std::string s = header.to_string ();
s += "\nstart=" + start.to_string ();
s += " maxage=" + std::to_string (age);
s += " count=" + std::to_string (count);
return s;
nano::message::operator() (obs); // Write common data
obs.write ("start", start);
obs.write ("age", age);
obs.write ("count", count);
}
/*
@ -859,13 +841,13 @@ void nano::bulk_pull::set_count_present (bool value_a)
header.extensions.set (count_present_flag, value_a);
}
std::string nano::bulk_pull::to_string () const
void nano::bulk_pull::operator() (nano::object_stream & obs) const
{
std::string s = header.to_string ();
s += "\nstart=" + start.to_string ();
s += " end=" + end.to_string ();
s += " cnt=" + std::to_string (count);
return s;
nano::message::operator() (obs); // Write common data
obs.write ("start", start);
obs.write ("end", end);
obs.write ("count", count);
}
/*
@ -917,27 +899,13 @@ bool nano::bulk_pull_account::deserialize (nano::stream & stream_a)
return error;
}
std::string nano::bulk_pull_account::to_string () const
void nano::bulk_pull_account::operator() (nano::object_stream & obs) const
{
std::string s = header.to_string () + "\n";
s += "acc=" + account.to_string ();
s += " min=" + minimum_amount.to_string ();
switch (flags)
{
case bulk_pull_account_flags::pending_hash_and_amount:
s += " pending_hash_and_amount";
break;
case bulk_pull_account_flags::pending_address_only:
s += " pending_address_only";
break;
case bulk_pull_account_flags::pending_hash_amount_and_address:
s += " pending_hash_amount_and_address";
break;
default:
s += " unknown flags";
break;
}
return s;
nano::message::operator() (obs); // Write common data
obs.write ("account", account);
obs.write ("minimum_amount", minimum_amount);
obs.write ("flags", static_cast<uint8_t> (flags)); // TODO: Prettier flag printing
}
/*
@ -970,6 +938,11 @@ void nano::bulk_push::visit (nano::message_visitor & visitor_a) const
visitor_a.bulk_push (*this);
}
void nano::bulk_push::operator() (nano::object_stream & obs) const
{
nano::message::operator() (obs); // Write common data
}
/*
* telemetry_req
*/
@ -1000,9 +973,9 @@ void nano::telemetry_req::visit (nano::message_visitor & visitor_a) const
visitor_a.telemetry_req (*this);
}
std::string nano::telemetry_req::to_string () const
void nano::telemetry_req::operator() (nano::object_stream & obs) const
{
return header.to_string ();
nano::message::operator() (obs); // Write common data
}
/*
@ -1080,18 +1053,14 @@ bool nano::telemetry_ack::is_empty_payload () const
return size () == 0;
}
std::string nano::telemetry_ack::to_string () const
void nano::telemetry_ack::operator() (nano::object_stream & obs) const
{
std::string s = header.to_string () + "\n";
if (is_empty_payload ())
nano::message::operator() (obs); // Write common data
if (!is_empty_payload ())
{
s += "empty telemetry payload";
obs.write ("data", data);
}
else
{
s += data.to_string ();
}
return s;
}
/*
@ -1247,15 +1216,6 @@ nano::error nano::telemetry_data::deserialize_json (nano::jsonconfig & json, boo
return json.get_error ();
}
std::string nano::telemetry_data::to_string () const
{
nano::jsonconfig jc;
serialize_json (jc, true);
std::stringstream ss;
jc.write (ss);
return ss.str ();
}
bool nano::telemetry_data::operator== (nano::telemetry_data const & data_a) const
{
return (signature == data_a.signature && node_id == data_a.node_id && block_count == data_a.block_count && cemented_count == data_a.cemented_count && unchecked_count == data_a.unchecked_count && account_count == data_a.account_count && bandwidth_cap == data_a.bandwidth_cap && uptime == data_a.uptime && peer_count == data_a.peer_count && protocol_version == data_a.protocol_version && genesis_block == data_a.genesis_block && major_version == data_a.major_version && minor_version == data_a.minor_version && patch_version == data_a.patch_version && pre_release_version == data_a.pre_release_version && maker == data_a.maker && timestamp == data_a.timestamp && active_difficulty == data_a.active_difficulty && unknown_data == data_a.unknown_data);
@ -1289,6 +1249,11 @@ bool nano::telemetry_data::validate_signature () const
return nano::validate_message (node_id, bytes.data (), bytes.size (), signature);
}
void nano::telemetry_data::operator() (nano::object_stream & obs) const
{
// TODO: Telemetry data
}
/*
* node_id_handshake
*/
@ -1406,19 +1371,12 @@ std::size_t nano::node_id_handshake::size (nano::message_header const & header)
return result;
}
std::string nano::node_id_handshake::to_string () const
void nano::node_id_handshake::operator() (nano::object_stream & obs) const
{
std::string s = header.to_string ();
if (query)
{
s += "\ncookie=" + query->cookie.to_string ();
}
if (response)
{
s += "\nresp_node_id=" + response->node_id.to_string ();
s += "\nresp_sig=" + response->signature.to_string ();
}
return s;
nano::message::operator() (obs); // Write common data
obs.write ("query", query);
obs.write ("response", response);
}
/*
@ -1435,6 +1393,11 @@ void nano::node_id_handshake::query_payload::deserialize (nano::stream & stream)
nano::read (stream, cookie);
}
void nano::node_id_handshake::query_payload::operator() (nano::object_stream & obs) const
{
obs.write ("cookie", cookie);
}
/*
* node_id_handshake::response_payload
*/
@ -1518,6 +1481,19 @@ bool nano::node_id_handshake::response_payload::validate (const nano::uint256_un
return true; // OK
}
void nano::node_id_handshake::response_payload::operator() (nano::object_stream & obs) const
{
obs.write ("node_id", node_id);
obs.write ("signature", signature);
obs.write ("v2", v2.has_value ());
if (v2)
{
obs.write ("salt", v2->salt);
obs.write ("genesis", v2->genesis);
}
}
/*
* asc_pull_req
*/
@ -1648,35 +1624,16 @@ bool nano::asc_pull_req::verify_consistency () const
return true; // Just for convenience of calling from asserts
}
std::string nano::asc_pull_req::to_string () const
void nano::asc_pull_req::operator() (nano::object_stream & obs) const
{
std::string s = header.to_string () + "\n";
nano::message::operator() (obs); // Write common data
std::visit ([&s] (auto && arg) {
using T = std::decay_t<decltype (arg)>;
obs.write ("type", type);
obs.write ("id", id);
if constexpr (std::is_same_v<T, nano::empty_payload>)
{
s += "missing payload";
}
else if constexpr (std::is_same_v<T, nano::asc_pull_req::blocks_payload>)
{
s += "acc:" + arg.start.to_string ();
s += " max block count:" + to_string_hex (static_cast<uint16_t> (arg.count));
s += " hash type:" + to_string_hex (static_cast<uint16_t> (arg.start_type));
}
else if constexpr (std::is_same_v<T, nano::asc_pull_req::account_info_payload>)
{
s += "target:" + arg.target.to_string ();
s += " hash type:" + to_string_hex (static_cast<uint16_t> (arg.target_type));
}
},
payload);
return s;
std::visit ([&obs] (auto && pld) { pld (obs); }, payload); // Log payload
}
/*
* asc_pull_req::blocks_payload
*/
@ -1695,6 +1652,13 @@ void nano::asc_pull_req::blocks_payload::deserialize (nano::stream & stream)
nano::read (stream, start_type);
}
void nano::asc_pull_req::blocks_payload::operator() (nano::object_stream & obs) const
{
obs.write ("start", start);
obs.write ("start_type", start_type);
obs.write ("count", count);
}
/*
* asc_pull_req::account_info_payload
*/
@ -1711,6 +1675,12 @@ void nano::asc_pull_req::account_info_payload::deserialize (stream & stream)
nano::read (stream, target_type);
}
void nano::asc_pull_req::account_info_payload::operator() (nano::object_stream & obs) const
{
obs.write ("target", target);
obs.write ("target_type", target_type);
}
/*
* asc_pull_req::frontiers_payload
*/
@ -1727,6 +1697,12 @@ void nano::asc_pull_req::frontiers_payload::deserialize (nano::stream & stream)
nano::read_big_endian (stream, count);
}
void nano::asc_pull_req::frontiers_payload::operator() (nano::object_stream & obs) const
{
obs.write ("start", start);
obs.write ("count", count);
}
/*
* asc_pull_ack
*/
@ -1858,43 +1834,14 @@ bool nano::asc_pull_ack::verify_consistency () const
return true; // Just for convenience of calling from asserts
}
std::string nano::asc_pull_ack::to_string () const
void nano::asc_pull_ack::operator() (nano::object_stream & obs) const
{
std::string s = header.to_string () + "\n";
nano::message::operator() (obs); // Write common data
std::visit ([&s] (auto && arg) {
using T = std::decay_t<decltype (arg)>;
obs.write ("type", type);
obs.write ("id", id);
if constexpr (std::is_same_v<T, nano::empty_payload>)
{
s += "missing payload";
}
else if constexpr (std::is_same_v<T, nano::asc_pull_ack::blocks_payload>)
{
auto block = std::begin (arg.blocks);
auto end_block = std::end (arg.blocks);
while (block != end_block)
{
s += (*block)->to_json ();
++block;
}
}
else if constexpr (std::is_same_v<T, nano::asc_pull_ack::account_info_payload>)
{
s += "account public key:" + arg.account.to_account ();
s += " account open:" + arg.account_open.to_string ();
s += " account head:" + arg.account_head.to_string ();
s += " block count:" + to_string_hex (arg.account_block_count);
s += " confirmation frontier:" + arg.account_conf_frontier.to_string ();
s += " confirmation height:" + to_string_hex (arg.account_conf_height);
}
},
payload);
return s;
std::visit ([&obs] (auto && pld) { pld (obs); }, payload); // Log payload
}
/*
@ -1924,6 +1871,11 @@ void nano::asc_pull_ack::blocks_payload::deserialize (nano::stream & stream)
}
}
void nano::asc_pull_ack::blocks_payload::operator() (nano::object_stream & obs) const
{
obs.write_range ("blocks", blocks);
}
/*
* asc_pull_ack::account_info_payload
*/
@ -1948,6 +1900,16 @@ void nano::asc_pull_ack::account_info_payload::deserialize (nano::stream & strea
nano::read_big_endian (stream, account_conf_height);
}
void nano::asc_pull_ack::account_info_payload::operator() (nano::object_stream & obs) const
{
obs.write ("account", account);
obs.write ("open", account_open);
obs.write ("head", account_head);
obs.write ("block_count", account_block_count);
obs.write ("conf_frontier", account_conf_frontier);
obs.write ("conf_height", account_conf_height);
}
/*
* asc_pull_ack::frontiers_payload
*/
@ -1989,6 +1951,15 @@ void nano::asc_pull_ack::frontiers_payload::deserialize (nano::stream & stream)
}
}
void nano::asc_pull_ack::frontiers_payload::operator() (nano::object_stream & obs) const
{
obs.write_range ("frontiers", frontiers, [] (auto const & entry, nano::object_stream & obs) {
auto & [account, hash] = entry;
obs.write ("account", account);
obs.write ("hash", hash);
});
}
/*
*
*/
@ -2004,3 +1975,10 @@ nano::stat::detail nano::to_stat_detail (nano::message_type type)
debug_assert (value);
return value.value_or (nano::stat::detail{});
}
nano::log::detail nano::to_log_detail (nano::message_type type)
{
auto value = magic_enum::enum_cast<nano::log::detail> (magic_enum::enum_name (type));
debug_assert (value);
return value.value_or (nano::log::detail{});
}

View file

@ -5,7 +5,10 @@
#include <nano/lib/config.hpp>
#include <nano/lib/errors.hpp>
#include <nano/lib/jsonconfig.hpp>
#include <nano/lib/logging.hpp>
#include <nano/lib/memory.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/object_stream.hpp>
#include <nano/lib/stats_enums.hpp>
#include <nano/lib/stream.hpp>
#include <nano/node/common.hpp>
@ -46,6 +49,7 @@ enum class message_type : uint8_t
std::string_view to_string (nano::message_type);
stat::detail to_stat_detail (nano::message_type);
log::detail to_log_detail (nano::message_type);
enum class bulk_pull_account_flags : uint8_t
{
@ -67,8 +71,6 @@ public:
void serialize (nano::stream &) const;
bool deserialize (nano::stream &);
std::string to_string () const;
public: // Payload
nano::networks network;
uint8_t version_max;
@ -112,6 +114,9 @@ public:
static extensions_bitset_t constexpr count_v2_mask_left{ 0xf000 };
static extensions_bitset_t constexpr count_v2_mask_right{ 0x00f0 };
static extensions_bitset_t constexpr telemetry_size_mask{ 0x3ff };
public: // Logging
void operator() (nano::object_stream &) const;
};
class message
@ -130,6 +135,9 @@ public:
public:
nano::message_header header;
public: // Logging
virtual void operator() (nano::object_stream &) const;
};
class keepalive final : public message
@ -143,7 +151,9 @@ public:
bool operator== (nano::keepalive const &) const;
std::array<nano::endpoint, 8> peers;
static std::size_t constexpr size = 8 * (16 + 2);
std::string to_string () const;
public: // Logging
void operator() (nano::object_stream &) const override;
};
class publish final : public message
@ -157,7 +167,9 @@ public:
bool operator== (nano::publish const &) const;
std::shared_ptr<nano::block> block;
nano::uint128_t digest{ 0 };
std::string to_string () const;
public: // Logging
void operator() (nano::object_stream &) const override;
};
class confirm_req final : public message
@ -172,7 +184,6 @@ public:
void visit (nano::message_visitor &) const override;
bool operator== (nano::confirm_req const &) const;
std::string roots_string () const;
std::string to_string () const;
static std::size_t size (nano::message_header const &);
@ -181,6 +192,9 @@ private:
public: // Payload
std::vector<std::pair<nano::block_hash, nano::root>> roots_hashes;
public: // Logging
void operator() (nano::object_stream &) const override;
};
class confirm_ack final : public message
@ -192,7 +206,6 @@ public:
void serialize (nano::stream &) const override;
void visit (nano::message_visitor &) const override;
bool operator== (nano::confirm_ack const &) const;
std::string to_string () const;
static std::size_t size (nano::message_header const &);
@ -201,6 +214,9 @@ private:
public: // Payload
std::shared_ptr<nano::vote> vote;
public: // Logging
void operator() (nano::object_stream &) const override;
};
class frontier_req final : public message
@ -216,7 +232,9 @@ public:
uint32_t age;
uint32_t count;
static std::size_t constexpr size = sizeof (start) + sizeof (age) + sizeof (count);
std::string to_string () const;
public: // Logging
void operator() (nano::object_stream &) const override;
};
enum class telemetry_maker : uint8_t
@ -256,13 +274,16 @@ public:
bool validate_signature () const;
bool operator== (nano::telemetry_data const &) const;
bool operator!= (nano::telemetry_data const &) const;
std::string to_string () const;
// Size does not include unknown_data
static auto constexpr size = sizeof (signature) + sizeof (node_id) + sizeof (block_count) + sizeof (cemented_count) + sizeof (unchecked_count) + sizeof (account_count) + sizeof (bandwidth_cap) + sizeof (peer_count) + sizeof (protocol_version) + sizeof (uptime) + sizeof (genesis_block) + sizeof (major_version) + sizeof (minor_version) + sizeof (patch_version) + sizeof (pre_release_version) + sizeof (maker) + sizeof (uint64_t) + sizeof (active_difficulty);
static auto constexpr latest_size = size; // This needs to be updated for each new telemetry version
private:
void serialize_without_signature (nano::stream &) const;
public: // Logging
void operator() (nano::object_stream &) const;
};
class telemetry_req final : public message
@ -273,7 +294,9 @@ public:
void serialize (nano::stream &) const override;
bool deserialize (nano::stream &);
void visit (nano::message_visitor &) const override;
std::string to_string () const;
public: // Logging
void operator() (nano::object_stream &) const override;
};
class telemetry_ack final : public message
@ -287,9 +310,11 @@ public:
bool deserialize (nano::stream &);
uint16_t size () const;
bool is_empty_payload () const;
std::string to_string () const;
static uint16_t size (nano::message_header const &);
nano::telemetry_data data;
public: // Logging
void operator() (nano::object_stream &) const override;
};
class bulk_pull final : public message
@ -309,7 +334,9 @@ public:
static std::size_t constexpr count_present_flag = nano::message_header::bulk_pull_count_present_flag;
static std::size_t constexpr extended_parameters_size = 8;
static std::size_t constexpr size = sizeof (start) + sizeof (end);
std::string to_string () const;
public: // Logging
void operator() (nano::object_stream &) const override;
};
class bulk_pull_account final : public message
@ -324,7 +351,9 @@ public:
nano::amount minimum_amount;
bulk_pull_account_flags flags;
static std::size_t constexpr size = sizeof (account) + sizeof (minimum_amount) + sizeof (bulk_pull_account_flags);
std::string to_string () const;
public: // Logging
void operator() (nano::object_stream &) const override;
};
class bulk_push final : public message
@ -335,6 +364,9 @@ public:
void serialize (nano::stream &) const override;
bool deserialize (nano::stream &);
void visit (nano::message_visitor &) const override;
public: // Logging
void operator() (nano::object_stream &) const override;
};
class node_id_handshake final : public message
@ -350,6 +382,9 @@ public: // Payload definitions
public:
nano::uint256_union cookie;
public: // Logging
void operator() (nano::object_stream &) const;
};
class response_payload
@ -380,6 +415,9 @@ public: // Payload definitions
static std::size_t constexpr size_v1 = sizeof (nano::account) + sizeof (nano::signature);
static std::size_t constexpr size_v2 = sizeof (nano::account) + sizeof (nano::signature) + sizeof (v2_payload);
static std::size_t size (nano::message_header const &);
public: // Logging
void operator() (nano::object_stream &) const;
};
public:
@ -392,7 +430,6 @@ public:
void visit (nano::message_visitor &) const override;
std::size_t size () const;
static std::size_t size (nano::message_header const &);
std::string to_string () const;
public: // Header
static uint8_t constexpr query_flag = 0;
@ -407,6 +444,9 @@ public: // Header
public: // Payload
std::optional<query_payload> query;
std::optional<response_payload> response;
public: // Logging
void operator() (nano::object_stream &) const override;
};
/**
@ -432,6 +472,10 @@ struct empty_payload
{
debug_assert (false);
}
void operator() (nano::object_stream &) const
{
debug_assert (false);
}
};
/**
@ -459,7 +503,6 @@ public:
void serialize_payload (nano::stream &) const;
void deserialize_payload (nano::stream &);
std::string to_string () const;
private: // Debug
/**
@ -479,10 +522,13 @@ public: // Payload definitions
void serialize (nano::stream &) const;
void deserialize (nano::stream &);
// Payload
public: // Payload
nano::hash_or_account start{ 0 };
uint8_t count{ 0 };
hash_type start_type{};
public: // Logging
void operator() (nano::object_stream &) const;
};
struct account_info_payload
@ -490,9 +536,12 @@ public: // Payload definitions
void serialize (nano::stream &) const;
void deserialize (nano::stream &);
// Payload
public: // Payload
nano::hash_or_account target{ 0 };
hash_type target_type{};
public: // Logging
void operator() (nano::object_stream &) const;
};
struct frontiers_payload
@ -500,9 +549,12 @@ public: // Payload definitions
void serialize (nano::stream &) const;
void deserialize (nano::stream &);
// Payload
public: // Payload
nano::account start{ 0 };
uint16_t count{ 0 };
public: // Logging
void operator() (nano::object_stream &) const;
};
public: // Payload
@ -515,6 +567,9 @@ public: // Payload
public:
/** Size of message without payload */
constexpr static std::size_t partial_size = sizeof (type) + sizeof (id);
public: // Logging
void operator() (nano::object_stream &) const override;
};
/**
@ -542,7 +597,6 @@ public:
void serialize_payload (nano::stream &) const;
void deserialize_payload (nano::stream &);
std::string to_string () const;
private: // Debug
/**
@ -559,8 +613,11 @@ public: // Payload definitions
void serialize (nano::stream &) const;
void deserialize (nano::stream &);
// Payload
public: // Payload
std::vector<std::shared_ptr<nano::block>> blocks;
public: // Logging
void operator() (nano::object_stream &) const;
};
struct account_info_payload
@ -568,13 +625,16 @@ public: // Payload definitions
void serialize (nano::stream &) const;
void deserialize (nano::stream &);
// Payload
public: // Payload
nano::account account{ 0 };
nano::block_hash account_open{ 0 };
nano::block_hash account_head{ 0 };
uint64_t account_block_count{ 0 };
nano::block_hash account_conf_frontier{ 0 };
uint64_t account_conf_height{ 0 };
public: // Logging
void operator() (nano::object_stream &) const;
};
struct frontiers_payload
@ -590,8 +650,11 @@ public: // Payload definitions
static void serialize_frontier (nano::stream &, frontier const &);
static frontier deserialize_frontier (nano::stream &);
// Payload
public: // Payload
std::vector<frontier> frontiers;
public: // Logging
void operator() (nano::object_stream &) const;
};
public: // Payload
@ -604,6 +667,9 @@ public: // Payload
public:
/** Size of message without payload */
constexpr static std::size_t partial_size = sizeof (type) + sizeof (id);
public: // Logging
void operator() (nano::object_stream &) const override;
};
class message_visitor

View file

@ -468,7 +468,8 @@ private:
void nano::network::process_message (nano::message const & message, std::shared_ptr<nano::transport::channel> const & channel)
{
node.stats.inc (nano::stat::type::message, to_stat_detail (message.header.type), nano::stat::dir::in);
node.stats.inc (nano::stat::type::message, to_stat_detail (message.type ()), nano::stat::dir::in);
node.logger.trace (nano::log::type::network_processed, to_log_detail (message.type ()), nano::log::arg{ "message", message });
network_message_visitor visitor{ node, channel };
message.visit (visitor);

View file

@ -185,8 +185,8 @@ nano::node::node (boost::asio::io_context & io_ctx_a, std::filesystem::path cons
vote_uniquer{},
confirmation_height_processor (ledger, write_database_queue, config.conf_height_processor_batch_min_time, logger, node_initialized_latch, flags.confirmation_height_processor_mode),
vote_cache{ config.vote_cache, stats },
generator{ config, ledger, wallets, vote_processor, history, network, stats, /* non-final */ false },
final_generator{ config, ledger, wallets, vote_processor, history, network, stats, /* final */ true },
generator{ config, ledger, wallets, vote_processor, history, network, stats, logger, /* non-final */ false },
final_generator{ config, ledger, wallets, vote_processor, history, network, stats, logger, /* final */ true },
active (*this, confirmation_height_processor),
scheduler_impl{ std::make_unique<nano::scheduler::component> (*this) },
scheduler{ *scheduler_impl },
@ -1298,6 +1298,8 @@ void nano::node::process_confirmed (nano::election_status const & status_a, uint
decltype (iteration_a) const num_iters = (config.block_processor_batch_max_time / network_params.node.process_confirmed_interval) * 4;
if (auto block_l = ledger.store.block.get (ledger.store.tx_begin_read (), hash))
{
logger.trace (nano::log::type::node, nano::log::detail::process_confirmed, nano::log::arg{ "block", block_l });
confirmation_height_processor.add (block_l);
}
else if (iteration_a < num_iters)

View file

@ -51,12 +51,21 @@ bool nano::scheduler::priority::activate (nano::account const & account_a, store
debug_assert (block != nullptr);
if (node.ledger.dependents_confirmed (transaction, *block))
{
stats.inc (nano::stat::type::election_scheduler, nano::stat::detail::activated);
auto balance = node.ledger.balance (transaction, hash);
auto previous_balance = node.ledger.balance (transaction, conf_info.frontier);
auto const balance = node.ledger.balance (transaction, hash);
auto const previous_balance = node.ledger.balance (transaction, conf_info.frontier);
auto const balance_priority = std::max (balance, previous_balance);
node.stats.inc (nano::stat::type::election_scheduler, nano::stat::detail::activated);
node.logger.trace (nano::log::type::election_scheduler, nano::log::detail::block_activated,
nano::log::arg{ "account", account_a.to_account () }, // TODO: Convert to lazy eval
nano::log::arg{ "block", block },
nano::log::arg{ "time", info->modified },
nano::log::arg{ "priority", balance_priority });
nano::lock_guard<nano::mutex> lock{ mutex };
buckets->push (info->modified, block, std::max (balance, previous_balance));
buckets->push (info->modified, block, balance_priority);
notify ();
return true; // Activated
}
}

View file

@ -23,12 +23,20 @@ void nano::transport::channel::send (nano::message & message_a, std::function<vo
if (!is_droppable_by_limiter || should_pass)
{
node.stats.inc (nano::stat::type::message, detail, nano::stat::dir::out);
node.logger.trace (nano::log::type::channel_sent, nano::to_log_detail (message_a.type ()),
nano::log::arg{ "message", message_a },
nano::log::arg{ "channel", *this },
nano::log::arg{ "dropped", false });
send_buffer (buffer, callback_a, drop_policy_a, traffic_type);
}
else
{
node.stats.inc (nano::stat::type::drop, detail, nano::stat::dir::out);
node.logger.trace (nano::log::type::channel_sent, nano::to_log_detail (message_a.type ()),
nano::log::arg{ "message", message_a },
nano::log::arg{ "channel", *this },
nano::log::arg{ "dropped", true });
if (callback_a)
{
@ -58,3 +66,10 @@ nano::endpoint nano::transport::channel::get_peering_endpoint () const
return get_endpoint ();
}
}
void nano::transport::channel::operator() (nano::object_stream & obs) const
{
obs.write ("endpoint", get_endpoint ());
obs.write ("peering_endpoint", get_peering_endpoint ());
obs.write ("node_id", get_node_id ());
}

View file

@ -1,6 +1,7 @@
#pragma once
#include <nano/lib/locks.hpp>
#include <nano/lib/object_stream.hpp>
#include <nano/lib/stats.hpp>
#include <nano/node/bandwidth_limiter.hpp>
#include <nano/node/common.hpp>
@ -140,6 +141,9 @@ private:
protected:
nano::node & node;
public: // Logging
virtual void operator() (nano::object_stream &) const;
};
}

View file

@ -13,6 +13,8 @@
#include <memory>
#include <utility>
#include <magic_enum.hpp>
/*
* socket
*/
@ -351,6 +353,14 @@ nano::tcp_endpoint nano::transport::socket::local_endpoint () const
return local;
}
void nano::transport::socket::operator() (nano::object_stream & obs) const
{
obs.write ("remote_endpoint", remote_endpoint ());
obs.write ("local_endpoint", local_endpoint ());
obs.write ("type", type_m);
obs.write ("endpoint_type", endpoint_type_m);
}
/*
* write_queue
*/
@ -458,18 +468,7 @@ std::size_t network_prefix)
return counted_connections;
}
std::string nano::transport::socket_type_to_string (nano::transport::socket::type_t type)
std::string_view nano::transport::to_string (nano::transport::socket::type_t type)
{
switch (type)
{
case nano::transport::socket::type_t::undefined:
return "undefined";
case nano::transport::socket::type_t::bootstrap:
return "bootstrap";
case nano::transport::socket::type_t::realtime:
return "realtime";
case nano::transport::socket::type_t::realtime_response_server:
return "realtime_response_server";
}
return "n/a";
return magic_enum::enum_name (type);
}

View file

@ -207,9 +207,12 @@ private:
public:
std::size_t const max_queue_size;
public: // Logging
virtual void operator() (nano::object_stream &) const;
};
std::string socket_type_to_string (socket::type_t type);
std::string_view to_string (socket::type_t type);
using address_socket_mmap = std::multimap<boost::asio::ip::address, std::weak_ptr<socket>>;

View file

@ -112,6 +112,13 @@ void nano::transport::channel_tcp::set_endpoint ()
}
}
void nano::transport::channel_tcp::operator() (nano::object_stream & obs) const
{
nano::transport::channel::operator() (obs); // Write common data
obs.write ("socket", socket);
}
/*
* tcp_channels
*/

View file

@ -93,7 +93,11 @@ namespace transport
private:
nano::tcp_endpoint endpoint{ boost::asio::ip::address_v6::any (), 0 };
public: // Logging
void operator() (nano::object_stream &) const override;
};
class tcp_channels final
{
friend class nano::transport::channel_tcp;

View file

@ -174,6 +174,10 @@ nano::vote_code nano::vote_processor::vote_blocking (std::shared_ptr<nano::vote>
break;
}
logger.trace (nano::log::type::vote_processor, nano::log::detail::vote_processed,
nano::log::arg{ "vote", vote_a },
nano::log::arg{ "result", result });
return result;
}

View file

@ -161,7 +161,7 @@ std::unique_ptr<nano::container_info_component> nano::collect_container_info (na
return composite;
}
nano::vote_generator::vote_generator (nano::node_config const & config_a, nano::ledger & ledger_a, nano::wallets & wallets_a, nano::vote_processor & vote_processor_a, nano::local_vote_history & history_a, nano::network & network_a, nano::stats & stats_a, bool is_final_a) :
nano::vote_generator::vote_generator (nano::node_config const & config_a, nano::ledger & ledger_a, nano::wallets & wallets_a, nano::vote_processor & vote_processor_a, nano::local_vote_history & history_a, nano::network & network_a, nano::stats & stats_a, nano::logger & logger_a, bool is_final_a) :
config (config_a),
ledger (ledger_a),
wallets (wallets_a),
@ -170,6 +170,7 @@ nano::vote_generator::vote_generator (nano::node_config const & config_a, nano::
spacing{ config_a.network_params.voting.delay },
network (network_a),
stats (stats_a),
logger (logger_a),
is_final (is_final_a),
vote_generation_queue{ stats, nano::stat::type::vote_generator, nano::thread_role::name::vote_generator_queue, /* single threaded */ 1, /* max queue size */ 1024 * 32, /* max batch size */ 1024 * 4 }
{
@ -185,18 +186,23 @@ nano::vote_generator::~vote_generator ()
bool nano::vote_generator::should_vote (store::write_transaction const & transaction, nano::root const & root_a, nano::block_hash const & hash_a)
{
auto block = ledger.store.block.get (transaction, hash_a);
bool should_vote = false;
if (is_final)
{
auto block (ledger.store.block.get (transaction, hash_a));
should_vote = block != nullptr && ledger.dependents_confirmed (transaction, *block) && ledger.store.final_vote.put (transaction, block->qualified_root (), hash_a);
debug_assert (block == nullptr || root_a == block->root ());
}
else
{
auto block (ledger.store.block.get (transaction, hash_a));
should_vote = block != nullptr && ledger.dependents_confirmed (transaction, *block);
}
logger.trace (nano::log::type::vote_generator, nano::log::detail::should_vote,
nano::log::arg{ "should_vote", should_vote },
nano::log::arg{ "block", block },
nano::log::arg{ "is_final", is_final });
return should_vote;
}

View file

@ -1,6 +1,7 @@
#pragma once
#include <nano/lib/locks.hpp>
#include <nano/lib/logging.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/processing_queue.hpp>
#include <nano/lib/utility.hpp>
@ -121,7 +122,7 @@ private:
using queue_entry_t = std::pair<nano::root, nano::block_hash>;
public:
vote_generator (nano::node_config const & config_a, nano::ledger & ledger_a, nano::wallets & wallets_a, nano::vote_processor & vote_processor_a, nano::local_vote_history & history_a, nano::network & network_a, nano::stats & stats_a, bool is_final_a);
vote_generator (nano::node_config const &, nano::ledger &, nano::wallets &, nano::vote_processor &, nano::local_vote_history &, nano::network &, nano::stats &, nano::logger &, bool is_final);
~vote_generator ();
/** Queue items for vote generation, or broadcast votes already in cache */
@ -159,6 +160,7 @@ private: // Dependencies
nano::vote_spacing spacing;
nano::network & network;
nano::stats & stats;
nano::logger & logger;
private:
processing_queue<queue_entry_t> vote_generation_queue;

View file

@ -6,6 +6,7 @@
#include <nano/lib/config.hpp>
#include <nano/lib/epoch.hpp>
#include <nano/lib/numbers.hpp>
#include <nano/lib/object_stream.hpp>
#include <nano/lib/rep_weights.hpp>
#include <nano/lib/stats.hpp>
#include <nano/lib/timer.hpp>

View file

@ -180,4 +180,12 @@ uint64_t nano::vote::packed_timestamp (uint64_t timestamp, uint8_t duration)
bool nano::vote::is_final_timestamp (uint64_t timestamp)
{
return timestamp == std::numeric_limits<uint64_t>::max ();
}
void nano::vote::operator() (nano::object_stream & obs) const
{
obs.write ("account", account);
obs.write ("final", is_final_timestamp (timestamp_m));
obs.write ("timestamp", timestamp_m);
obs.write_range ("hashes", hashes);
}

View file

@ -71,6 +71,9 @@ private:
static std::string const hash_prefix;
static uint64_t packed_timestamp (uint64_t timestamp, uint8_t duration);
public: // Logging
void operator() (nano::object_stream &) const;
};
using vote_uniquer = nano::uniquer<nano::block_hash, nano::vote>;