diff --git a/Makefile.am b/Makefile.am index 37c66c2723..ead0a906a2 100755 --- a/Makefile.am +++ b/Makefile.am @@ -82,6 +82,7 @@ src_libbitcoin_system_la_SOURCES = \ src/config/transaction.cpp \ src/config/url.cpp \ src/config/utilities.cpp \ + src/config/version.cpp \ src/crypto/aes256.cpp \ src/crypto/der_parser.cpp \ src/crypto/ec_context.cpp \ @@ -261,6 +262,7 @@ test_libbitcoin_system_test_SOURCES = \ test/config/printer.cpp \ test/config/url.cpp \ test/config/utilities.cpp \ + test/config/version.cpp \ test/crypto/aes256.cpp \ test/crypto/elliptic_curve.cpp \ test/crypto/pseudo_random.cpp \ @@ -533,7 +535,8 @@ include_bitcoin_system_config_HEADERS = \ include/bitcoin/system/config/script.hpp \ include/bitcoin/system/config/transaction.hpp \ include/bitcoin/system/config/url.hpp \ - include/bitcoin/system/config/utilities.hpp + include/bitcoin/system/config/utilities.hpp \ + include/bitcoin/system/config/version.hpp include_bitcoin_system_cryptodir = ${includedir}/bitcoin/system/crypto include_bitcoin_system_crypto_HEADERS = \ diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index 5c4ddbe36a..c39ed1cfbc 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -515,6 +515,7 @@ add_library( ${CANONICAL_LIB_NAME} "../../src/config/transaction.cpp" "../../src/config/url.cpp" "../../src/config/utilities.cpp" + "../../src/config/version.cpp" "../../src/crypto/aes256.cpp" "../../src/crypto/der_parser.cpp" "../../src/crypto/ec_context.cpp" @@ -740,6 +741,7 @@ if (with-tests) "../../test/config/printer.cpp" "../../test/config/url.cpp" "../../test/config/utilities.cpp" + "../../test/config/version.cpp" "../../test/crypto/aes256.cpp" "../../test/crypto/elliptic_curve.cpp" "../../test/crypto/pseudo_random.cpp" diff --git a/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj b/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj index bf96c9857f..034c8c48fc 100644 --- a/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj @@ -161,6 +161,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj.filters index 43f0d7bbaf..55ea6c40bb 100644 --- a/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj.filters @@ -219,6 +219,9 @@ src\config + + src\config + src diff --git a/builds/msvc/vs2022/libbitcoin-system/libbitcoin-system.vcxproj b/builds/msvc/vs2022/libbitcoin-system/libbitcoin-system.vcxproj index 29d3fa8ec5..f60c785e59 100644 --- a/builds/msvc/vs2022/libbitcoin-system/libbitcoin-system.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-system/libbitcoin-system.vcxproj @@ -195,6 +195,7 @@ + @@ -357,6 +358,7 @@ + diff --git a/builds/msvc/vs2022/libbitcoin-system/libbitcoin-system.vcxproj.filters b/builds/msvc/vs2022/libbitcoin-system/libbitcoin-system.vcxproj.filters index 05e1e4e235..96733c97c8 100644 --- a/builds/msvc/vs2022/libbitcoin-system/libbitcoin-system.vcxproj.filters +++ b/builds/msvc/vs2022/libbitcoin-system/libbitcoin-system.vcxproj.filters @@ -381,6 +381,9 @@ src\config + + src\config + src\crypto @@ -821,6 +824,9 @@ include\bitcoin\system\config + + include\bitcoin\system\config + include\bitcoin\system diff --git a/include/bitcoin/system/chain/header.hpp b/include/bitcoin/system/chain/header.hpp index 96806ca82e..00fd8cb509 100644 --- a/include/bitcoin/system/chain/header.hpp +++ b/include/bitcoin/system/chain/header.hpp @@ -81,7 +81,6 @@ class BC_API header void to_data(std::ostream& stream) const NOEXCEPT; void to_data(writer& sink) const NOEXCEPT; - /// Properties. /// ----------------------------------------------------------------------- /// Native properties. @@ -96,6 +95,7 @@ class BC_API header /// Computed properties. uint256_t proof() const NOEXCEPT; hash_digest hash() const NOEXCEPT; + double difficulty() const NOEXCEPT; /// Cache and metadata. /// ----------------------------------------------------------------------- diff --git a/include/bitcoin/system/config/config.hpp b/include/bitcoin/system/config/config.hpp index 43f8f6acb4..9f7ab489cd 100644 --- a/include/bitcoin/system/config/config.hpp +++ b/include/bitcoin/system/config/config.hpp @@ -41,5 +41,6 @@ #include #include #include +#include #endif diff --git a/include/bitcoin/system/config/version.hpp b/include/bitcoin/system/config/version.hpp new file mode 100644 index 0000000000..86fda560e8 --- /dev/null +++ b/include/bitcoin/system/config/version.hpp @@ -0,0 +1,104 @@ +/** + * Copyright (c) 2011-2025 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 . + */ +#ifndef LIBBITCOIN_SYSTEM_CONFIG_VERSION_HPP +#define LIBBITCOIN_SYSTEM_CONFIG_VERSION_HPP + +#include +#include + +namespace libbitcoin { +namespace system { +namespace config { + +/// Container for a protocol version tuple with up to four segments. +/// Segments are non-negative integers, padded with zeros for fewer than four. +/// The format is major.minor[.subminor[.patch]] with at least one dot. +class BC_API version +{ +public: + typedef std::shared_ptr ptr; + + DEFAULT_COPY_MOVE_DESTRUCT(version); + + version() NOEXCEPT; + + /// Deserialize from dotted string (throws on invalid format). + version(const std::string& version_str) THROWS; + + /// Construct from individual segments (2 to 4, pads with zeros). + version(uint32_t major, uint32_t minor, uint32_t subminor={}, + uint32_t patch={}) NOEXCEPT; + + /// Properties. + /// ----------------------------------------------------------------------- + + /// Access to the internal segments array. + const std::array& segments() const NOEXCEPT; + + /// Methods. + /// ----------------------------------------------------------------------- + + /// The version is 0.0.0.0. + bool is_default() const NOEXCEPT; + + /// Serialize to dot string, omitting trailing zero segments (minimum 2). + std::string to_string() const NOEXCEPT; + + /// Operators. + /// ----------------------------------------------------------------------- + + /// Deserialize from input stream (throws on invalid format). + friend std::istream& operator>>(std::istream& input, version& argument) THROWS; + + /// Serialize to output stream. + friend std::ostream& operator<<(std::ostream& output, + const version& argument) NOEXCEPT; + +private: + // This is not thread safe. + std::array segments_; +}; + +/// Lexicographical comparison. +BC_API bool operator==(const version& left, const version& right) NOEXCEPT; +BC_API bool operator!=(const version& left, const version& right) NOEXCEPT; +BC_API bool operator<(const version& left, const version& right) NOEXCEPT; +BC_API bool operator<=(const version& left, const version& right) NOEXCEPT; +BC_API bool operator>(const version& left, const version& right) NOEXCEPT; +BC_API bool operator>=(const version& left, const version& right) NOEXCEPT; + +typedef std::vector versions; + +} // namespace config +} // namespace system +} // namespace libbitcoin + +namespace std +{ +template<> +struct hash +{ + size_t operator()(const bc::system::config::version& value) const NOEXCEPT + { + return std::hash{}(value.to_string()); + } +}; +} // namespace std + +#endif diff --git a/include/bitcoin/system/impl/serial/deserialize.ipp b/include/bitcoin/system/impl/serial/deserialize.ipp index 91718427d2..4f492c3a68 100644 --- a/include/bitcoin/system/impl/serial/deserialize.ipp +++ b/include/bitcoin/system/impl/serial/deserialize.ipp @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -107,7 +108,14 @@ bool deserialize(Value& out, const std::string_view& text) NOEXCEPT // This can convert garbage to zero, use is_ascii_number for pre-assurance. try { - std::istringstream istream(trim_copy(text)); + auto trimmed = trim_copy(text); + if constexpr (is_integer && !is_signed) + { + if (!trimmed.empty() && trimmed.front() == '-') + return false; + } + + std::istringstream istream(std::move(trimmed)); istream >> out; return !istream.fail(); } diff --git a/src/chain/header.cpp b/src/chain/header.cpp index 725a204cbb..4c414a87b8 100644 --- a/src/chain/header.cpp +++ b/src/chain/header.cpp @@ -268,6 +268,29 @@ hash_digest header::hash() const NOEXCEPT return digest; } +// computed, not used in consensus. +double header::difficulty() const NOEXCEPT +{ + auto shift = bit_and(shift_right(bits_, 24), 0xff_u32); + auto difference = + static_cast(0x0000ffff_u32) / + static_cast(bit_and(bits_, 0x00ffffff_u32)); + + while (shift < 29u) + { + difference *= 256.0; + ++shift; + } + + while (shift > 29u) + { + difference /= 256.0; + --shift; + } + + return difference; +} + // Cache and metadata. // ---------------------------------------------------------------------------- diff --git a/src/config/version.cpp b/src/config/version.cpp new file mode 100644 index 0000000000..95d7e209c4 --- /dev/null +++ b/src/config/version.cpp @@ -0,0 +1,154 @@ +/** + * Copyright (c) 2011-2025 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 + +#include +#include +#include +#include +#include +#include + +namespace libbitcoin { +namespace system { +namespace config { + +// Contructors. +// ---------------------------------------------------------------------------- + +// Default version is 0.0.0.0 and serializes as "0.0". +version::version() NOEXCEPT + : segments_{} +{ +} + +version::version(const std::string& version) THROWS + : segments_{} +{ + std::istringstream(version) >> (*this); +} + +version::version(uint32_t major, uint32_t minor, uint32_t subminor, + uint32_t patch) NOEXCEPT + : segments_{ major, minor, subminor, patch } +{ +} + +// Properties. +// ---------------------------------------------------------------------------- + +const std::array& version::segments() const NOEXCEPT +{ + return segments_; +} + +// Methods. +// ---------------------------------------------------------------------------- + +bool version::is_default() const NOEXCEPT +{ + constexpr std::array default_{}; + return segments_ == default_; +} + +std::string version::to_string() const NOEXCEPT +{ + std::ostringstream value{}; + value << (*this); + BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) + return value.str(); + BC_POP_WARNING() +} + +// Operators. +// ---------------------------------------------------------------------------- + +bool operator==(const version& left, const version& right) NOEXCEPT +{ + return left.segments() == right.segments(); +} + +bool operator!=(const version& left, const version& right) NOEXCEPT +{ + return !(left == right); +} + +bool operator<(const version& left, const version& right) NOEXCEPT +{ + return std::lexicographical_compare( + left.segments().begin(), left.segments().end(), + right.segments().begin(), right.segments().end()); +} + +bool operator<=(const version& left, const version& right) NOEXCEPT +{ + return left < right || left == right; +} + +bool operator>(const version& left, const version& right) NOEXCEPT +{ + return right < left; +} + +bool operator>=(const version& left, const version& right) NOEXCEPT +{ + return right <= left; +} + +std::istream& operator>>(std::istream& input, + version& argument) THROWS +{ + std::string value; + input >> value; + + using namespace system; + const auto tokens = system::split(value, "."); + const auto count = tokens.size(); + if (count < 2u || count > 4u) + throw istream_exception(value); + + argument = {}; + for (size_t index{}; index < count; ++index) + if (!deserialize(argument.segments_.at(index), tokens.at(index))) + throw istream_exception(value); + + return input; +} + +std::ostream& operator<<(std::ostream& output, + const version& argument) NOEXCEPT +{ + size_t last{ 3 }; + while (last > 1u && is_zero(argument.segments_.at(last))) + --last; + + for (size_t index{}; index <= last; ++index) + { + BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) + if (!is_zero(index)) output << '.'; + output << argument.segments_.at(index); + BC_POP_WARNING() + } + + return output; +} + +} // namespace config +} // namespace system +} // namespace libbitcoin diff --git a/test/chain/header.cpp b/test/chain/header.cpp index 9c268e02ea..799bc7cbda 100644 --- a/test/chain/header.cpp +++ b/test/chain/header.cpp @@ -240,14 +240,20 @@ BOOST_AUTO_TEST_CASE(header__to_data__writer__expected) // properties // ---------------------------------------------------------------------------- -// hash - BOOST_AUTO_TEST_CASE(header__proof__genesis_block__expected) { const chain::block block{ settings(selection::mainnet).genesis_block }; BOOST_REQUIRE_EQUAL(block.header().proof(), 0x0000000100010001); } +// hash + +BOOST_AUTO_TEST_CASE(header__difficulty__genesis_block__expected) +{ + const chain::block block{ settings(selection::mainnet).genesis_block }; + BOOST_REQUIRE_EQUAL(block.header().difficulty(), 1.0); +} + // validation (public) // ---------------------------------------------------------------------------- diff --git a/test/config/version.cpp b/test/config/version.cpp new file mode 100644 index 0000000000..762eb78347 --- /dev/null +++ b/test/config/version.cpp @@ -0,0 +1,284 @@ +/** + * Copyright (c) 2011-2025 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(version_tests) + +using namespace bc::system::config; +using namespace boost::program_options; + +BOOST_AUTO_TEST_CASE(version__construct__default__zero_segments) +{ + const version instance; + const auto& segments = instance.segments(); + BOOST_REQUIRE_EQUAL(segments[0], 0u); + BOOST_REQUIRE_EQUAL(segments[1], 0u); + BOOST_REQUIRE_EQUAL(segments[2], 0u); + BOOST_REQUIRE_EQUAL(segments[3], 0u); + BOOST_REQUIRE(instance.is_default()); +} + +BOOST_AUTO_TEST_CASE(version__construct__non_default__expected_segments) +{ + const version instance("1.2.3.4"); + const auto& segments = instance.segments(); + BOOST_REQUIRE_EQUAL(segments[0], 1u); + BOOST_REQUIRE_EQUAL(segments[1], 2u); + BOOST_REQUIRE_EQUAL(segments[2], 3u); + BOOST_REQUIRE_EQUAL(segments[3], 4u); + BOOST_REQUIRE(!instance.is_default()); +} + +BOOST_AUTO_TEST_CASE(version__construct__string_invalid_too_few_segments__throws_istream_exception) +{ + BOOST_REQUIRE_THROW(version("1"), istream_exception); +} + +BOOST_AUTO_TEST_CASE(version__construct__string_invalid_too_many_segments__throws_istream_exception) +{ + BOOST_REQUIRE_THROW(version("1.2.3.4.5"), istream_exception); +} + +BOOST_AUTO_TEST_CASE(version__construct__string_invalid_non_numeric__throws_istream_exception) +{ + BOOST_REQUIRE_THROW(version("1.a"), istream_exception); +} + +BOOST_AUTO_TEST_CASE(version__construct__string_invalid_negative__throws_istream_exception) +{ + BOOST_REQUIRE_THROW(version("1.-2"), istream_exception); +} + +BOOST_AUTO_TEST_CASE(version__construct__string_invalid_out_of_range__throws_istream_exception) +{ + BOOST_REQUIRE_THROW(version("1.4294967296"), istream_exception); +} + +BOOST_AUTO_TEST_CASE(version__construct__segments_two__padded_zeros) +{ + const version instance(1u, 2u); + const auto& segments = instance.segments(); + BOOST_REQUIRE_EQUAL(segments[0], 1u); + BOOST_REQUIRE_EQUAL(segments[1], 2u); + BOOST_REQUIRE_EQUAL(segments[2], 0u); + BOOST_REQUIRE_EQUAL(segments[3], 0u); +} + +BOOST_AUTO_TEST_CASE(version__construct__segments_three__padded_zero) +{ + const version instance(1u, 2u, 3u); + const auto& segments = instance.segments(); + BOOST_REQUIRE_EQUAL(segments[0], 1u); + BOOST_REQUIRE_EQUAL(segments[1], 2u); + BOOST_REQUIRE_EQUAL(segments[2], 3u); + BOOST_REQUIRE_EQUAL(segments[3], 0u); +} + +BOOST_AUTO_TEST_CASE(version__construct__segments_four__expected) +{ + const version instance(1u, 2u, 3u, 4u); + const auto& segments = instance.segments(); + BOOST_REQUIRE_EQUAL(segments[0], 1u); + BOOST_REQUIRE_EQUAL(segments[1], 2u); + BOOST_REQUIRE_EQUAL(segments[2], 3u); + BOOST_REQUIRE_EQUAL(segments[3], 4u); +} + +BOOST_AUTO_TEST_CASE(version__is_default__default__true) +{ + const version instance; + BOOST_REQUIRE(instance.is_default()); +} + +BOOST_AUTO_TEST_CASE(version__is_default__non_default__false) +{ + const version instance(1u, 0u); + BOOST_REQUIRE(!instance.is_default()); +} + +BOOST_AUTO_TEST_CASE(version__is_default__zero_segments__true) +{ + const version instance(0u, 0u, 0u, 0u); + BOOST_REQUIRE(instance.is_default()); +} + +BOOST_AUTO_TEST_CASE(version__is_default__partial_zeros__false) +{ + const version instance(0u, 0u, 1u, 0u); + BOOST_REQUIRE(!instance.is_default()); +} + +BOOST_AUTO_TEST_CASE(version__to_string__two_segments__expected) +{ + const version instance(1u, 2u); + BOOST_REQUIRE_EQUAL(instance.to_string(), "1.2"); +} + +BOOST_AUTO_TEST_CASE(version__to_string__three_segments__expected_no_trailing_zero) +{ + const version instance(1u, 2u, 3u); + BOOST_REQUIRE_EQUAL(instance.to_string(), "1.2.3"); +} + +BOOST_AUTO_TEST_CASE(version__to_string__four_segments_trailing_zero__omits_trailing_zero) +{ + const version instance(1u, 2u, 3u, 0u); + BOOST_REQUIRE_EQUAL(instance.to_string(), "1.2.3"); +} + +BOOST_AUTO_TEST_CASE(version__to_string__default__minimum_two_segments) +{ + const version instance; + BOOST_REQUIRE_EQUAL(instance.to_string(), "0.0"); +} + +BOOST_AUTO_TEST_CASE(version__operator_shift_right__valid__deserializes) +{ + version instance; + std::istringstream input("1.2.3"); + input >> instance; + const auto& segments = instance.segments(); + BOOST_REQUIRE_EQUAL(segments[0], 1u); + BOOST_REQUIRE_EQUAL(segments[1], 2u); + BOOST_REQUIRE_EQUAL(segments[2], 3u); + BOOST_REQUIRE_EQUAL(segments[3], 0u); +} + +BOOST_AUTO_TEST_CASE(version__operator_shift_right__invalid__throws_istream_exception) +{ + version instance; + std::istringstream input("1.a"); + BOOST_REQUIRE_THROW(input >> instance, istream_exception); +} + +BOOST_AUTO_TEST_CASE(version__operator_shift_left__valid__serializes) +{ + const version instance(1u, 2u, 3u); + std::ostringstream output; + output << instance; + BOOST_REQUIRE_EQUAL(output.str(), "1.2.3"); +} + +BOOST_AUTO_TEST_CASE(version__operator_equal__same__true) +{ + const version a(1u, 2u, 3u, 4u); + const version b(1u, 2u, 3u, 4u); + BOOST_REQUIRE(a == b); +} + +BOOST_AUTO_TEST_CASE(version__operator_equal__different__false) +{ + const version a(1u, 2u, 3u, 4u); + const version b(1u, 2u, 3u, 5u); + BOOST_REQUIRE(!(a == b)); +} + +BOOST_AUTO_TEST_CASE(version__operator_not_equal__same__false) +{ + const version a(1u, 2u, 3u, 4u); + const version b(1u, 2u, 3u, 4u); + BOOST_REQUIRE(!(a != b)); +} + +BOOST_AUTO_TEST_CASE(version__operator_not_equal__different__true) +{ + const version a(1u, 2u, 3u, 4u); + const version b(1u, 2u, 3u, 5u); + BOOST_REQUIRE(a != b); +} + +BOOST_AUTO_TEST_CASE(version__operator_less__prefix_shorter__true) +{ + const version a(1u, 2u, 3u); + const version b(1u, 2u, 3u, 1u); + BOOST_REQUIRE(a < b); +} + +BOOST_AUTO_TEST_CASE(version__operator_less__prefix_longer__false) +{ + const version a(1u, 2u, 3u, 1u); + const version b(1u, 2u, 3u); + BOOST_REQUIRE(!(a < b)); +} + +BOOST_AUTO_TEST_CASE(version__operator_less__differing_component__expected) +{ + const version a(1u, 2u, 4u); + const version b(1u, 3u, 0u); + BOOST_REQUIRE(a < b); +} + +BOOST_AUTO_TEST_CASE(version__operator_less_equal__equal__true) +{ + const version a(1u, 2u, 3u, 4u); + const version b(1u, 2u, 3u, 4u); + BOOST_REQUIRE(a <= b); +} + +BOOST_AUTO_TEST_CASE(version__operator_less_equal__less__true) +{ + const version a(1u, 2u, 3u); + const version b(1u, 2u, 3u, 1u); + BOOST_REQUIRE(a <= b); +} + +BOOST_AUTO_TEST_CASE(version__operator_less_equal__greater__false) +{ + const version a(1u, 2u, 3u, 1u); + const version b(1u, 2u, 3u); + BOOST_REQUIRE(!(a <= b)); +} + +BOOST_AUTO_TEST_CASE(version__operator_greater__prefix_longer__true) +{ + const version a(1u, 2u, 3u, 1u); + const version b(1u, 2u, 3u); + BOOST_REQUIRE(a > b); +} + +BOOST_AUTO_TEST_CASE(version__operator_greater__prefix_shorter__false) +{ + const version a(1u, 2u, 3u); + const version b(1u, 2u, 3u, 1u); + BOOST_REQUIRE(!(a > b)); +} + +BOOST_AUTO_TEST_CASE(version__operator_greater_equal__equal__true) +{ + const version a(1u, 2u, 3u, 4u); + const version b(1u, 2u, 3u, 4u); + BOOST_REQUIRE(a >= b); +} + +BOOST_AUTO_TEST_CASE(version__operator_greater_equal__greater__true) +{ + const version a(1u, 2u, 3u, 1u); + const version b(1u, 2u, 3u); + BOOST_REQUIRE(a >= b); +} + +BOOST_AUTO_TEST_CASE(version__operator_greater_equal__less__false) +{ + const version a(1u, 2u, 3u); + const version b(1u, 2u, 3u, 1u); + BOOST_REQUIRE(!(a >= b)); +} + +BOOST_AUTO_TEST_SUITE_END()