diff --git a/Makefile.am b/Makefile.am index 40c6b282..99c0ca6a 100644 --- a/Makefile.am +++ b/Makefile.am @@ -80,8 +80,11 @@ check_PROGRAMS = test/libbitcoin-node-test test_libbitcoin_node_test_CPPFLAGS = -I${srcdir}/include ${bitcoin_database_BUILD_CPPFLAGS} ${bitcoin_network_BUILD_CPPFLAGS} test_libbitcoin_node_test_LDADD = src/libbitcoin-node.la ${boost_unit_test_framework_LIBS} ${bitcoin_database_LIBS} ${bitcoin_network_LIBS} test_libbitcoin_node_test_SOURCES = \ + test/block_arena.cpp \ + test/block_memory.cpp \ test/configuration.cpp \ test/error.cpp \ + test/full_node.cpp \ test/main.cpp \ test/node.cpp \ test/settings.cpp \ diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index c73cfaee..62d3b604 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -320,8 +320,11 @@ target_link_libraries( ${CANONICAL_LIB_NAME} #------------------------------------------------------------------------------ if (with-tests) add_executable( libbitcoin-node-test + "../../test/block_arena.cpp" + "../../test/block_memory.cpp" "../../test/configuration.cpp" "../../test/error.cpp" + "../../test/full_node.cpp" "../../test/main.cpp" "../../test/node.cpp" "../../test/settings.cpp" diff --git a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj index a3ac4147..95c230f1 100644 --- a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj @@ -70,6 +70,8 @@ + + @@ -81,6 +83,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters index 0fc4ebb1..c05046ef 100644 --- a/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-node-test/libbitcoin-node-test.vcxproj.filters @@ -21,6 +21,12 @@ + + src + + + src + src\chasers @@ -54,6 +60,9 @@ src + + src + src diff --git a/include/bitcoin/node/block_arena.hpp b/include/bitcoin/node/block_arena.hpp index 157aaf0b..1b089049 100644 --- a/include/bitcoin/node/block_arena.hpp +++ b/include/bitcoin/node/block_arena.hpp @@ -27,7 +27,7 @@ namespace libbitcoin { namespace node { /// Thread UNSAFE detachable linked-linear memory arena. -class BCN_API block_arena final +class BCN_API block_arena : public arena { public: @@ -35,7 +35,7 @@ class BCN_API block_arena final block_arena(size_t multiple) NOEXCEPT; block_arena(block_arena&& other) NOEXCEPT; - ~block_arena() NOEXCEPT; + virtual ~block_arena() NOEXCEPT; block_arena& operator=(block_arena&& other) NOEXCEPT; @@ -49,6 +49,33 @@ class BCN_API block_arena final void release(void* address) NOEXCEPT override; protected: + /// Determine alignment offset. + static constexpr size_t to_aligned(size_t value, size_t align) NOEXCEPT + { + using namespace system; + BC_ASSERT_MSG(is_nonzero(align), "align zero"); + BC_ASSERT_MSG(!is_add_overflow(value, sub1(align)), "overflow"); + BC_ASSERT_MSG(power2(floored_log2(align)) == align, "align power"); + BC_ASSERT_MSG(align <= alignof(std::max_align_t), "align overflow"); + return (value + sub1(align)) & ~sub1(align); + } + + /// Malloc throws if memory is not allocated. + virtual INLINE void* malloc(size_t bytes) THROWS + { + BC_PUSH_WARNING(NO_MALLOC_OR_FREE) + return std::malloc(bytes); + BC_POP_WARNING() + } + + /// Free does not throw, behavior is undefined if address is incorrect. + virtual INLINE void free(void* address) NOEXCEPT + { + BC_PUSH_WARNING(NO_MALLOC_OR_FREE) + std::free(address); + BC_POP_WARNING() + } + /// Link a memory chunk to the allocated stack. void push(size_t minimum=zero) THROWS; @@ -79,17 +106,6 @@ class BCN_API block_arena final return system::floored_subtract(size_, offset_); } -private: - constexpr size_t to_aligned(size_t value, size_t align) NOEXCEPT - { - using namespace system; - BC_ASSERT_MSG(is_nonzero(align), "align zero"); - BC_ASSERT_MSG(!is_add_overflow(value, sub1(align)), "overflow"); - BC_ASSERT_MSG(power2(floored_log2(align)) == align, "align power"); - BC_ASSERT_MSG(align <= alignof(std::max_align_t), "align overflow"); - return (value + sub1(align)) & ~sub1(align); - } - void* do_allocate(size_t bytes, size_t align) THROWS override; void do_deallocate(void* ptr, size_t bytes, size_t align) NOEXCEPT override; bool do_is_equal(const arena& other) const NOEXCEPT override; @@ -100,7 +116,6 @@ class BCN_API block_arena final size_t offset_; size_t total_; size_t size_; - }; } // namespace node diff --git a/include/bitcoin/node/block_memory.hpp b/include/bitcoin/node/block_memory.hpp index 310d6e43..47106251 100644 --- a/include/bitcoin/node/block_memory.hpp +++ b/include/bitcoin/node/block_memory.hpp @@ -28,7 +28,7 @@ namespace libbitcoin { namespace node { /// Thread SAFE linked-linear arena allocator. -class BCN_API block_memory final +class BCN_API block_memory : public network::memory { public: @@ -41,9 +41,9 @@ class BCN_API block_memory final /// Each thread obtains an arena. arena* get_arena() NOEXCEPT override; -private: +protected: // This is thread safe. - std::atomic_size_t count_{}; + std::atomic_size_t count_{ zero }; // This is protected by constructor init and thread_local indexation. std::vector arenas_{}; diff --git a/src/block_arena.cpp b/src/block_arena.cpp index 1cc246d7..8212c604 100644 --- a/src/block_arena.cpp +++ b/src/block_arena.cpp @@ -52,7 +52,6 @@ block_arena::block_arena(block_arena&& other) NOEXCEPT block_arena::~block_arena() NOEXCEPT { - release(memory_map_); } block_arena& block_arena::operator=(block_arena&& other) NOEXCEPT @@ -96,11 +95,7 @@ void block_arena::release(void* address) NOEXCEPT while (!is_null(address)) { const auto link = get_link(pointer_cast(address)); - - BC_PUSH_WARNING(NO_MALLOC_OR_FREE) - std::free(address); - BC_POP_WARNING() - + free(address); address = link; } } @@ -115,10 +110,7 @@ void block_arena::push(size_t minimum) THROWS // Ensure next allocation accomodates link plus current request. BC_ASSERT(!is_add_overflow(minimum, link_size)); size_ = std::max(size_, minimum + link_size); - - BC_PUSH_WARNING(NO_MALLOC_OR_FREE) - const auto map = pointer_cast(std::malloc(size_)); - BC_POP_WARNING() + const auto map = pointer_cast(malloc(size_)); if (is_null(map)) throw allocation_exception{}; diff --git a/test/block_arena.cpp b/test/block_arena.cpp new file mode 100644 index 00000000..a3c51e83 --- /dev/null +++ b/test/block_arena.cpp @@ -0,0 +1,320 @@ +/** + * Copyright (c) 2011-2024 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "test.hpp" + +BOOST_AUTO_TEST_SUITE(block_arena_tests) + +using namespace system; + +class accessor + : public block_arena +{ +public: + // public + + using block_arena::block_arena; + + const uint8_t* get_memory_map() const NOEXCEPT + { + return memory_map_; + } + + void set_memory_map(uint8_t* map) NOEXCEPT + { + memory_map_ = map; + } + + size_t get_multiple() const NOEXCEPT + { + return multiple_; + } + + void set_multiple(size_t multiple) NOEXCEPT + { + multiple_ = multiple; + } + + size_t get_offset() const NOEXCEPT + { + return offset_; + } + + void set_offset(size_t offset) NOEXCEPT + { + offset_ = offset; + } + + size_t get_total() const NOEXCEPT + { + return total_; + } + + void set_total(size_t total) NOEXCEPT + { + total_ = total; + } + + size_t get_size() const NOEXCEPT + { + return size_; + } + + void set_size(size_t size) NOEXCEPT + { + size_ = size; + } + + void release(void* address) NOEXCEPT override + { + block_arena::release(address); + } + + // protected + + static constexpr size_t to_aligned_(size_t value, size_t align) NOEXCEPT + { + return to_aligned(value, align); + } + + void* malloc(size_t bytes) THROWS override + { + stack.emplace_back(bytes); + return stack.back().data(); + } + + void free(void* address) NOEXCEPT override + { + freed.push_back(address); + } + + void push_(size_t minimum=zero) THROWS + { + push(minimum); + } + + void set_link_(uint8_t* next_address) NOEXCEPT + { + set_link(next_address); + } + + void* get_link_(uint8_t* address) const NOEXCEPT + { + return get_link(address); + } + + size_t capacity_() const NOEXCEPT + { + return capacity(); + } + + // These can be directly invoked via public methods. + //void* do_allocate(size_t bytes, size_t align) THROWS override; + //void do_deallocate(void* ptr, size_t bytes, size_t align) NOEXCEPT override; + //bool do_is_equal(const arena& other) const NOEXCEPT override; + + data_stack stack{}; + std::vector freed{}; +}; + +// construct + +BOOST_AUTO_TEST_CASE(block_arena__construct__zero__sets_zero) +{ + constexpr auto multiple = zero; + const accessor instance{ multiple }; + BOOST_REQUIRE_EQUAL(instance.get_memory_map(), nullptr); + BOOST_REQUIRE_EQUAL(instance.get_multiple(), multiple); + BOOST_REQUIRE_EQUAL(instance.get_offset(), zero); + BOOST_REQUIRE_EQUAL(instance.get_total(), zero); + BOOST_REQUIRE_EQUAL(instance.get_size(), zero); +} + +BOOST_AUTO_TEST_CASE(block_arena__construct__value__sets_multiple) +{ + constexpr auto multiple = 42u; + const accessor instance{ multiple }; + BOOST_REQUIRE_EQUAL(instance.get_memory_map(), nullptr); + BOOST_REQUIRE_EQUAL(instance.get_multiple(), multiple); + BOOST_REQUIRE_EQUAL(instance.get_offset(), zero); + BOOST_REQUIRE_EQUAL(instance.get_total(), zero); + BOOST_REQUIRE_EQUAL(instance.get_size(), zero); +} + +BOOST_AUTO_TEST_CASE(block_arena__move_construct__always__nulls_memory_map) +{ + constexpr auto multiple = 42u; + accessor instance{ multiple }; + system::data_chunk value{ 0x00 }; + auto address = value.data(); + instance.set_memory_map(address); + BOOST_REQUIRE_EQUAL(instance.get_memory_map(), address); + + const accessor copy{ std::move(instance) }; + BOOST_REQUIRE_EQUAL(instance.get_memory_map(), nullptr); + BOOST_REQUIRE_EQUAL(copy.get_memory_map(), address); + BOOST_REQUIRE_EQUAL(copy.get_multiple(), multiple); + BOOST_REQUIRE_EQUAL(copy.get_offset(), zero); + BOOST_REQUIRE_EQUAL(copy.get_total(), zero); + BOOST_REQUIRE_EQUAL(copy.get_size(), zero); +} + +// assign + +BOOST_AUTO_TEST_CASE(block_arena__assign__always__nulls_memory_map) +{ + constexpr auto multiple = 42u; + accessor instance{ multiple }; + system::data_chunk value{ 0x00 }; + auto address = value.data(); + instance.set_memory_map(address); + BOOST_REQUIRE_EQUAL(instance.get_memory_map(), address); + + const accessor copy = std::move(instance); + BOOST_REQUIRE_EQUAL(instance.get_memory_map(), nullptr); + BOOST_REQUIRE_EQUAL(copy.get_memory_map(), address); + BOOST_REQUIRE_EQUAL(copy.get_multiple(), multiple); + BOOST_REQUIRE_EQUAL(copy.get_offset(), zero); + BOOST_REQUIRE_EQUAL(copy.get_total(), zero); + BOOST_REQUIRE_EQUAL(copy.get_size(), zero); +} + +// start + +BOOST_AUTO_TEST_CASE(block_arena__start__multiple_overflow__throws_allocation_exception) +{ + accessor instance{ two }; + + BC_PUSH_WARNING(DISCARDING_NON_DISCARDABLE) + BOOST_REQUIRE_THROW(instance.start(max_size_t), allocation_exception); + BC_POP_WARNING() +} + +BOOST_AUTO_TEST_CASE(block_arena__start__zero__minimal_allocation) +{ + constexpr auto size = zero; + constexpr auto multiple = 42u; + accessor instance{ multiple }; + const auto memory = instance.start(size); + BOOST_REQUIRE_NE(memory, nullptr); + BOOST_REQUIRE_LT(size, sizeof(void*)); + BOOST_REQUIRE_EQUAL(instance.stack.size(), one); + BOOST_REQUIRE_EQUAL(instance.stack.front().size(), sizeof(void*)); +} + +BOOST_AUTO_TEST_CASE(block_arena__start__at_least_pointer_size__expected) +{ + constexpr auto size = 42u; + constexpr auto multiple = 10u; + accessor instance{ multiple }; + const auto memory = instance.start(size); + BOOST_REQUIRE_NE(memory, nullptr); + BOOST_REQUIRE_GE(size, sizeof(void*)); + BOOST_REQUIRE_EQUAL(instance.stack.size(), one); + BOOST_REQUIRE_EQUAL(instance.stack.front().size(), size * multiple); + BOOST_REQUIRE_EQUAL(instance.stack.front().data(), instance.get_memory_map()); +} + +// detach + +BOOST_AUTO_TEST_CASE(block_arena__detach__unstarted__zero) +{ + accessor instance{ 42 }; + const auto size = instance.detach(); + BOOST_REQUIRE_EQUAL(size, zero); +} + +// release + +BOOST_AUTO_TEST_CASE(block_arena__release__nullptr__nop) +{ + accessor instance{ 42 }; + instance.release(nullptr); +} + +// to_aligned + +BOOST_AUTO_TEST_CASE(block_arena__to_aligned__zero_one__zero) +{ + constexpr auto aligned = accessor::to_aligned_(0, 1); + BOOST_REQUIRE_EQUAL(aligned, zero); +} + +// push + +BOOST_AUTO_TEST_CASE(block_arena__push__) +{ + accessor instance{ 42 }; + instance.push_(); +} + +// set_link + +BOOST_AUTO_TEST_CASE(block_arena__set_link__nullptr__nop) +{ + accessor instance{ 42 }; + instance.set_link_(nullptr); +} + +// get_link + +BOOST_AUTO_TEST_CASE(block_arena__get_link__unstarted__zero_filled) +{ + accessor instance{ 42 }; + instance.stack.emplace_back(sizeof(void*)); + const auto link = instance.get_link_(instance.stack.front().data()); + BOOST_REQUIRE_EQUAL(link, nullptr); +} + +// capacity + +BOOST_AUTO_TEST_CASE(block_arena__capacity__unstarted__zero) +{ + accessor instance{ 42 }; + const auto capacity = instance.capacity_(); + BOOST_REQUIRE_EQUAL(capacity, zero); +} + +// do_allocate/do_deallocate + +BOOST_AUTO_TEST_CASE(block_arena__do_allocate__do_deallocate__expected) +{ + accessor instance{ 42 }; + const auto block = instance.start(0); + BOOST_REQUIRE_NE(block, nullptr); + + const auto memory = instance.allocate(24, 4); + instance.deallocate(memory, 24, 4); +} + +// do_is_equal + +BOOST_AUTO_TEST_CASE(block_arena__do_is_equal__equal__true) +{ + accessor instance{ 42 }; + BOOST_REQUIRE(instance.is_equal(instance)); +} + +BOOST_AUTO_TEST_CASE(block_arena__do_is_equal__unequal__false) +{ + accessor instance1{ 42 }; + accessor instance2{ 42 }; + BOOST_REQUIRE(!instance1.is_equal(instance2)); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/block_memory.cpp b/test/block_memory.cpp new file mode 100644 index 00000000..e37bb38d --- /dev/null +++ b/test/block_memory.cpp @@ -0,0 +1,194 @@ +/** + * Copyright (c) 2011-2024 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "test.hpp" + +#include + +BOOST_AUTO_TEST_SUITE(block_memory_tests) + +class accessor + : public block_memory +{ +public: + using block_memory::block_memory; + + size_t get_count() const NOEXCEPT + { + return count_; + } + + size_t get_size() const NOEXCEPT + { + return arenas_.size(); + } + + arena* get_arena_at(size_t index) NOEXCEPT + { + return &arenas_.at(index); + } +}; + +BOOST_AUTO_TEST_CASE(block_memory__get_arena__no_multiple_no_threads__default_arena) +{ + constexpr size_t multiple = 0; + constexpr size_t threads = 0; + accessor instance{ multiple, threads }; + BOOST_REQUIRE_EQUAL(instance.get_size(), zero); + BOOST_REQUIRE_EQUAL(instance.get_count(), zero); + BOOST_REQUIRE_EQUAL(instance.get_arena(), default_arena::get()); +} + +BOOST_AUTO_TEST_CASE(block_memory__get_arena__no_threads__default_arena) +{ + constexpr size_t multiple = 42; + constexpr size_t threads = 0; + accessor instance{ multiple, threads }; + BOOST_REQUIRE_EQUAL(instance.get_size(), zero); + BOOST_REQUIRE_EQUAL(instance.get_count(), zero); + BOOST_REQUIRE_EQUAL(instance.get_arena(), default_arena::get()); +} + +BOOST_AUTO_TEST_CASE(block_memory__get_arena__no_multiple__default_arena) +{ + constexpr size_t multiple = 0; + constexpr size_t threads = 1; + accessor instance{ multiple, threads }; + BOOST_REQUIRE_EQUAL(instance.get_size(), zero); + BOOST_REQUIRE_EQUAL(instance.get_count(), zero); + BOOST_REQUIRE_EQUAL(instance.get_arena(), default_arena::get()); +} + +BOOST_AUTO_TEST_CASE(block_memory__get_arena__multiple_one_thread__not_default_arena) +{ + constexpr size_t multiple = 42; + constexpr size_t threads = 1; + accessor instance{ multiple, threads }; + BOOST_REQUIRE_EQUAL(instance.get_size(), one); + BOOST_REQUIRE_EQUAL(instance.get_count(), zero); + BOOST_REQUIRE_NE(instance.get_arena(), default_arena::get()); +} + +BOOST_AUTO_TEST_CASE(block_memory__get_arena__multiple_threads__count_unincremented) +{ + constexpr size_t multiple = 42; + constexpr size_t threads = 2; + accessor instance{ multiple, threads }; + BOOST_REQUIRE_EQUAL(instance.get_size(), two); + + // On any given thread count must not change. + BOOST_REQUIRE_EQUAL(instance.get_count(), zero); + BOOST_REQUIRE_NE(instance.get_arena(), default_arena::get()); + BOOST_REQUIRE_EQUAL(instance.get_count(), zero); + BOOST_REQUIRE_NE(instance.get_arena(), default_arena::get()); + BOOST_REQUIRE_EQUAL(instance.get_count(), zero); +} + +BOOST_AUTO_TEST_CASE(block_memory__get_arena__two_threads__independent_not_default_arenas) +{ + // non-zero multiple ensures block_arenas but value is otherwise unimportant. + constexpr size_t multiple = 42; + constexpr size_t threads = 2; + accessor instance{ multiple, threads }; + size_t count1{}; + size_t count2{}; + void* arena1{}; + void* arena2{}; + + std::thread thread1([&]() NOEXCEPT + { + count1 = instance.get_count(); + arena1 = instance.get_arena(); + }); + + std::thread thread2([&]() NOEXCEPT + { + count2 = instance.get_count(); + arena2 = instance.get_arena(); + }); + + thread1.join(); + thread2.join(); + + BOOST_REQUIRE_NE(arena2, default_arena::get()); + BOOST_REQUIRE_NE(arena1, default_arena::get()); + BOOST_REQUIRE_NE(arena1, arena2); + BOOST_REQUIRE_NE(count1, count2); +} + +BOOST_AUTO_TEST_CASE(block_memory__get_arena__overflow_threads__default_arena) +{ + constexpr size_t multiple = 42; + constexpr size_t threads = 2; + accessor instance{ multiple, threads }; + size_t count1a{}; + size_t count1b{}; + size_t count2a{}; + size_t count2b{}; + size_t count3a{}; + size_t count3b{}; + void* arena1{}; + void* arena2{}; + void* arena3{}; + + // order is required to ensure third is the overflow. + std::thread thread1([&]() NOEXCEPT + { + count1a = instance.get_count(); + arena1 = instance.get_arena(); + count1b = instance.get_count(); + + std::thread thread2([&]() NOEXCEPT + { + count2a = instance.get_count(); + arena2 = instance.get_arena(); + count2b = instance.get_count(); + + std::thread thread3([&]() NOEXCEPT + { + count3a = instance.get_count(); + arena3 = instance.get_arena(); + count3b = instance.get_count(); + }); + + thread3.join(); + }); + + thread2.join(); + }); + + thread1.join(); + + // Arenas are ordered by thread order above. + BOOST_REQUIRE_EQUAL(arena1, instance.get_arena_at(0)); + BOOST_REQUIRE_EQUAL(arena2, instance.get_arena_at(1)); + BOOST_REQUIRE_NE(arena1, arena2); + + // Index overflows to default arena. + BOOST_REQUIRE_EQUAL(arena3, default_arena::get()); + + // Count reflects total, not the index of the thread. + BOOST_REQUIRE_EQUAL(count1a, 0u); + BOOST_REQUIRE_EQUAL(count2a, 1u); + BOOST_REQUIRE_EQUAL(count3a, 2u); + BOOST_REQUIRE_EQUAL(count1b, 1u); + BOOST_REQUIRE_EQUAL(count2b, 2u); + BOOST_REQUIRE_EQUAL(count3b, 3u); +} + +BOOST_AUTO_TEST_SUITE_END() diff --git a/test/full_node.cpp b/test/full_node.cpp new file mode 100644 index 00000000..c7a51955 --- /dev/null +++ b/test/full_node.cpp @@ -0,0 +1,28 @@ +/** + * Copyright (c) 2011-2024 libbitcoin developers (see AUTHORS) + * + * This file is part of libbitcoin. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ +#include "test.hpp" + +BOOST_AUTO_TEST_SUITE(full_node_tests) + +BOOST_AUTO_TEST_CASE(full_node_test) +{ + BOOST_REQUIRE(true); +} + +BOOST_AUTO_TEST_SUITE_END()