From 62066035a10f2562423739e5f68600441eaa47bc Mon Sep 17 00:00:00 2001 From: evoskuil Date: Thu, 11 Apr 2024 16:45:36 -0400 Subject: [PATCH] Expand and test malleability functions. --- Makefile.am | 1 + builds/cmake/CMakeLists.txt | 1 + .../libbitcoin-system-test.vcxproj | 1 + .../libbitcoin-system-test.vcxproj.filters | 3 + include/bitcoin/system/chain/block.hpp | 23 +- src/chain/block.cpp | 64 ++- test/chain/block_malleable.cpp | 410 ++++++++++++++++++ 7 files changed, 481 insertions(+), 22 deletions(-) create mode 100644 test/chain/block_malleable.cpp diff --git a/Makefile.am b/Makefile.am index f17e906f9d..f3ec61be19 100755 --- a/Makefile.am +++ b/Makefile.am @@ -215,6 +215,7 @@ test_libbitcoin_system_test_SOURCES = \ test/typelets.cpp \ test/types.cpp \ test/chain/block.cpp \ + test/chain/block_malleable.cpp \ test/chain/chain_state.cpp \ test/chain/checkpoint.cpp \ test/chain/compact.cpp \ diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt index 188f2f21ab..ac0c410c23 100644 --- a/builds/cmake/CMakeLists.txt +++ b/builds/cmake/CMakeLists.txt @@ -698,6 +698,7 @@ if (with-tests) "../../test/typelets.cpp" "../../test/types.cpp" "../../test/chain/block.cpp" + "../../test/chain/block_malleable.cpp" "../../test/chain/chain_state.cpp" "../../test/chain/checkpoint.cpp" "../../test/chain/compact.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 7c6c97d758..32ac11a3fe 100644 --- a/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj +++ b/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj @@ -73,6 +73,7 @@ $(IntDir)test_chain_block.obj + 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 9af35af52e..937bc0e2d6 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 @@ -105,6 +105,9 @@ src\chain + + src\chain + src\chain diff --git a/include/bitcoin/system/chain/block.hpp b/include/bitcoin/system/chain/block.hpp index 22c9852b38..f3657db684 100644 --- a/include/bitcoin/system/chain/block.hpp +++ b/include/bitcoin/system/chain/block.hpp @@ -41,6 +41,7 @@ class BC_API block public: DEFAULT_COPY_MOVE_DESTRUCT(block); + typedef std_vector sizes; typedef std::shared_ptr cptr; /// Constructors. @@ -90,13 +91,16 @@ class BC_API block uint64_t fees() const NOEXCEPT; uint64_t claim() const NOEXCEPT; hash_digest hash() const NOEXCEPT; - bool is_malleable() const NOEXCEPT; - bool is_malleable_duplicate() const NOEXCEPT; - bool is_malleable_coincident() const NOEXCEPT; bool is_segregated() const NOEXCEPT; size_t serialized_size(bool witness) const NOEXCEPT; size_t signature_operations(bool bip16, bool bip141) const NOEXCEPT; + /// Computed malleation properties. + bool is_malleable() const NOEXCEPT; + bool is_malleable64() const NOEXCEPT; + bool is_malleable32() const NOEXCEPT; + bool is_malleated32() const NOEXCEPT; + /// Validation. /// ----------------------------------------------------------------------- @@ -115,6 +119,19 @@ class BC_API block block(const chain::header::cptr& header, const chain::transactions_cptr& txs, bool valid) NOEXCEPT; + size_t malleated32_size() const NOEXCEPT; + bool is_malleated32(size_t width) const NOEXCEPT; + static constexpr bool is_malleable32_size(size_t set, size_t width) NOEXCEPT + { + // Malleable when set is odd at width depth and not before and not one. + // This is the only case in which Merkle clones the last item in a set. + for (auto depth = one; depth <= width; depth *= two, set = to_half(set)) + if (is_odd(set)) + return depth == width && !is_one(set); + + return false; + } + /// Check (context free). /// ----------------------------------------------------------------------- diff --git a/src/chain/block.cpp b/src/chain/block.cpp index dbf3339c23..055384f74b 100644 --- a/src/chain/block.cpp +++ b/src/chain/block.cpp @@ -438,40 +438,66 @@ bool block::is_hash_limit_exceeded() const NOEXCEPT return hashes.size() > hash_limit; } -// This is not part of validation. Should be called after *invalidation* to -// determine if the invalidity is universal (otherwise do not cache invalid), -// and as an abbreviated validation when under checkpoint/milestone. -// lists.linuxfoundation.org/pipermail/bitcoin-dev/2019-February/016697.html bool block::is_malleable() const NOEXCEPT { - return is_malleable_coincident() || is_malleable_duplicate(); + return is_malleable64() || is_malleable32(); } -// Repeated tx hashes is a subset of is_internal_double_spend. -// This form of malleability also implies current block instance is invalid. -bool block::is_malleable_duplicate() const NOEXCEPT +bool block::is_malleable32() const NOEXCEPT { - // A set is used to collapse duplicates. - std::set hashes; + const auto unmalleated = txs_->size(); + for (auto mally = one; mally <= unmalleated; mally *= two) + if (is_malleable32_size(unmalleated, mally)) + return true; - BC_PUSH_WARNING(NO_THROW_IN_NOEXCEPT) - for (const auto& tx: *txs_) - hashes.insert(tx->hash(false)); - BC_POP_WARNING() - - return hashes.size() == txs_->size(); + return false; +} + +bool block::is_malleated32() const NOEXCEPT +{ + return !is_zero(malleated32_size()); +} + +// protected +// The size of an actual malleation of this block, or zero. +size_t block::malleated32_size() const NOEXCEPT +{ + const auto malleated = txs_->size(); + for (auto mally = one; mally <= to_half(malleated); mally *= two) + if (is_malleable32_size(malleated - mally, mally) && + is_malleated32(mally)) + return mally; + + return zero; +} + +// protected +// True if the last width set of tx hashes repeats. +bool block::is_malleated32(size_t width) const NOEXCEPT +{ + const auto malleated = txs_->size(); + if (is_zero(width) || width > to_half(malleated)) + return false; + + auto mally = txs_->rbegin(); + auto legit = std::next(mally, width); + while (!is_zero(width--)) + if ((*mally++)->hash(false) != (*legit++)->hash(false)) + return false; + + return true; } // If all non-witness tx serializations are 64 bytes the id is malleable. // This form of malleability does not imply current block instance is invalid. -bool block::is_malleable_coincident() const NOEXCEPT +bool block::is_malleable64() const NOEXCEPT { - const auto two_leaf_size = [](const transaction::cptr& tx) NOEXCEPT + const auto two_leaves = [](const transaction::cptr& tx) NOEXCEPT { return tx->serialized_size(false) == two * hash_size; }; - return std::all_of(txs_->begin(), txs_->end(), two_leaf_size); + return !is_empty() && std::all_of(txs_->begin(), txs_->end(), two_leaves); } bool block::is_segregated() const NOEXCEPT diff --git a/test/chain/block_malleable.cpp b/test/chain/block_malleable.cpp new file mode 100644 index 0000000000..9a3a99cdb5 --- /dev/null +++ b/test/chain/block_malleable.cpp @@ -0,0 +1,410 @@ +/** + * Copyright (c) 2011-2023 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_tests) + +using namespace system::chain; + +constexpr auto previous = base16_hash("000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f"); +constexpr auto merkle = base16_hash("4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b"); + +static const chain::header header +{ + 10, + previous, + merkle, + 531234, + 6523454, + 68644 +}; + +class accessor + : public block +{ +public: + using block::block; + + size_t malleated32_size() const NOEXCEPT + { + return block::malleated32_size(); + } + + bool is_malleated32_(size_t width) const NOEXCEPT + { + return block::is_malleated32(width); + } + + static constexpr bool is_malleable32_size(size_t set, + size_t width) NOEXCEPT + { + return block::is_malleable32_size(set, width); + } +}; + +transaction tx60 +{ + 42, + inputs{ { point{}, script{}, 42 } }, + outputs{ { 42, script{} } }, + 42 +}; + +transaction tx61 +{ + 42, + inputs{ { point{}, script{ { opcode::dup } }, 42 } }, + outputs{ { 42, script{} } }, + 42 +}; + +transaction tx62 +{ + 42, + inputs{ { point{}, script{ { opcode::dup } }, 42 } }, + outputs{ { 42, script{ { opcode::dup } } } }, + 42 +}; + +transaction tx63 +{ + 42, + inputs{ { point{}, script{ { opcode::dup, opcode::dup } }, 42 } }, + outputs{ { 42, script{ { opcode::dup } } } }, + 42 +}; + +transaction tx64 +{ + 42, + inputs{ { point{}, script{ { opcode::dup, opcode::dup } }, 42 } }, + outputs{ { 42, script{ { opcode::dup, opcode::dup } } } }, + 42 +}; + +transaction tx65 +{ + 42, + inputs{ { point{}, script{ { opcode::dup, opcode::dup, opcode::dup } }, 42 } }, + outputs{ { 42, script{ { opcode::dup, opcode::dup } } } }, + 42 +}; + +transaction tx66 +{ + 42, + inputs{ { point{}, script{ { opcode::dup, opcode::dup, opcode::dup } }, 42 } }, + outputs{ { 42, script{ { opcode::dup, opcode::dup, opcode::dup } } } }, + 42 +}; + +BOOST_AUTO_TEST_CASE(block__transactions__sizes__expected) +{ + const accessor instance{}; + BOOST_REQUIRE_EQUAL(tx60.serialized_size(false), 60u); + BOOST_REQUIRE_EQUAL(tx61.serialized_size(false), 61u); + BOOST_REQUIRE_EQUAL(tx62.serialized_size(false), 62u); + BOOST_REQUIRE_EQUAL(tx63.serialized_size(false), 63u); + BOOST_REQUIRE_EQUAL(tx64.serialized_size(false), 64u); + BOOST_REQUIRE_EQUAL(tx65.serialized_size(false), 65u); + BOOST_REQUIRE_EQUAL(tx66.serialized_size(false), 66u); +} + +BOOST_AUTO_TEST_CASE(block__malleable__empty__false) +{ + const accessor instance{}; + BOOST_REQUIRE(!instance.is_malleable()); + BOOST_REQUIRE(!instance.is_malleable64()); + BOOST_REQUIRE(!instance.is_malleable32()); + BOOST_REQUIRE(!instance.is_malleable32_size(0, 0)); + BOOST_REQUIRE(!instance.is_malleated32()); + BOOST_REQUIRE(!instance.is_malleated32_(0)); +} + +// is_malleable + +BOOST_AUTO_TEST_CASE(block__is_malleable__64_not_32__true) +{ + const accessor instance{ header, { tx64 } }; + BOOST_REQUIRE(instance.is_malleable()); + BOOST_REQUIRE(instance.is_malleable64()); + BOOST_REQUIRE(!instance.is_malleable32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleable__32_not_64__true) +{ + const accessor instance{ header, { tx60, tx61, tx62, tx64, tx65, tx66 } }; + BOOST_REQUIRE(instance.is_malleable()); + BOOST_REQUIRE(!instance.is_malleable64()); + BOOST_REQUIRE(instance.is_malleable32()); +} + +// is_malleable32 + +BOOST_AUTO_TEST_CASE(block__is_malleable32__one_64__false) +{ + const accessor instance{ header, { tx64 } }; + BOOST_REQUIRE(!instance.is_malleable32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleable32__two__false) +{ + const accessor instance{ header, { tx64, tx65 } }; + BOOST_REQUIRE(!instance.is_malleable32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleable32__three__true) +{ + const accessor instance{ header, { tx64, tx65, tx66 } }; + BOOST_REQUIRE(instance.is_malleable32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleable32__four__false) +{ + const accessor instance{ header, { tx63, tx64, tx65, tx66 } }; + BOOST_REQUIRE(!instance.is_malleable32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleable32__five__true) +{ + const accessor instance{ header, { tx62, tx63, tx64, tx65, tx66 } }; + BOOST_REQUIRE(instance.is_malleable32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleable32__six__true) +{ + const accessor instance{ header, { tx61, tx62, tx63, tx64, tx65, tx66 } }; + BOOST_REQUIRE(instance.is_malleable32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleable32__six_duplicates__true) +{ + const accessor instance{ header, { tx61, tx62, tx63, tx62, tx65, tx62 } }; + BOOST_REQUIRE(instance.is_malleable32()); +} + +// is_malleated32 + +BOOST_AUTO_TEST_CASE(block__is_malleated32__one_64__false) +{ + const accessor instance{ header, { tx64 } }; + BOOST_REQUIRE(!instance.is_malleated32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleated32__two_distinct__false) +{ + const accessor instance{ header, { tx64, tx65 } }; + BOOST_REQUIRE(!instance.is_malleated32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleated32__two_same__false) +{ + const accessor instance{ header, { tx65, tx65 } }; + BOOST_REQUIRE(!instance.is_malleated32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleated32__three_two_duplicated__false) +{ + const accessor instance{ header, { tx64, tx65, tx65 } }; + BOOST_REQUIRE(!instance.is_malleated32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleated32__four_distinct__false) +{ + const accessor instance{ header, { tx63, tx64, tx65, tx66 } }; + BOOST_REQUIRE(!instance.is_malleated32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleated32__four_two_duplicated__false) +{ + const accessor instance{ header, { tx63, tx64, tx63, tx64 } }; + BOOST_REQUIRE(!instance.is_malleated32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleated32__five_two_duplicated__false) +{ + const accessor instance{ header, { tx62, tx63, tx64, tx66, tx66 } }; + BOOST_REQUIRE(!instance.is_malleated32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleated32__six_distinct__false) +{ + const accessor instance{ header, { tx61, tx62, tx63, tx64, tx65, tx66 } }; + BOOST_REQUIRE(!instance.is_malleated32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleated32__six_one_duplicated__true) +{ + const accessor instance{ header, { tx61, tx62, tx63, tx64, tx65, tx65 } }; + BOOST_REQUIRE(instance.is_malleated32()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleated32__eight_two_duplicated__true) +{ + const accessor instance{ header, { tx60, tx61, tx62, tx63, tx64, tx65, tx64, tx65 } }; + BOOST_REQUIRE(instance.is_malleated32()); +} + +// is_malleable64 + +BOOST_AUTO_TEST_CASE(block__is_malleable64__one_64__true) +{ + const accessor instance{ header, { tx64 } }; + BOOST_REQUIRE(instance.is_malleable64()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleable64__one_65__false) +{ + const accessor instance{ header, { tx65 } }; + BOOST_REQUIRE(!instance.is_malleable64()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleable64__two_64__true) +{ + const accessor instance{ header, { tx64, tx64 } }; + BOOST_REQUIRE(instance.is_malleable64()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleable64__two_64_65__false) +{ + const accessor instance{ header, { tx64, tx65 } }; + BOOST_REQUIRE(!instance.is_malleable64()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleable64__three_64_65_64__false) +{ + const accessor instance{ header, { tx64, tx65 } }; + BOOST_REQUIRE(!instance.is_malleable64()); +} + +BOOST_AUTO_TEST_CASE(block__is_malleable64__three_64_64_64__true) +{ + const accessor instance{ header, { tx64, tx64, tx64 } }; + BOOST_REQUIRE(instance.is_malleable64()); +} + +// is_malleable32_size + +BOOST_AUTO_TEST_CASE(block__is_malleable32_size__overflow__false) +{ + BOOST_REQUIRE(!accessor::is_malleable32_size(0, 1)); + BOOST_REQUIRE(!accessor::is_malleable32_size(1, 50)); + BOOST_REQUIRE(!accessor::is_malleable32_size(2, 100)); +} + +BOOST_AUTO_TEST_CASE(block__is_malleable32_size__various__expected) +{ + BOOST_REQUIRE(!accessor::is_malleable32_size(1, 1)); + + BOOST_REQUIRE(!accessor::is_malleable32_size(2, 1)); + BOOST_REQUIRE(!accessor::is_malleable32_size(2, 2)); + + BOOST_REQUIRE(accessor::is_malleable32_size(3, 1)); // 4:1 + BOOST_REQUIRE(!accessor::is_malleable32_size(3, 2)); + BOOST_REQUIRE(!accessor::is_malleable32_size(3, 3)); + + BOOST_REQUIRE(!accessor::is_malleable32_size(4, 1)); + BOOST_REQUIRE(!accessor::is_malleable32_size(4, 2)); + BOOST_REQUIRE(!accessor::is_malleable32_size(4, 3)); + BOOST_REQUIRE(!accessor::is_malleable32_size(4, 4)); + + BOOST_REQUIRE(accessor::is_malleable32_size(5, 1)); // 6:1 + BOOST_REQUIRE(!accessor::is_malleable32_size(5, 2)); + BOOST_REQUIRE(!accessor::is_malleable32_size(5, 3)); + BOOST_REQUIRE(!accessor::is_malleable32_size(5, 4)); + BOOST_REQUIRE(!accessor::is_malleable32_size(5, 5)); + + BOOST_REQUIRE(!accessor::is_malleable32_size(6, 1)); + BOOST_REQUIRE(accessor::is_malleable32_size(6, 2)); // 8:2 + BOOST_REQUIRE(!accessor::is_malleable32_size(6, 3)); + BOOST_REQUIRE(!accessor::is_malleable32_size(6, 4)); + BOOST_REQUIRE(!accessor::is_malleable32_size(6, 5)); + BOOST_REQUIRE(!accessor::is_malleable32_size(6, 6)); + + BOOST_REQUIRE(accessor::is_malleable32_size(7, 1)); // 8:1 + BOOST_REQUIRE(!accessor::is_malleable32_size(7, 2)); + BOOST_REQUIRE(!accessor::is_malleable32_size(7, 3)); + BOOST_REQUIRE(!accessor::is_malleable32_size(7, 4)); + BOOST_REQUIRE(!accessor::is_malleable32_size(7, 5)); + BOOST_REQUIRE(!accessor::is_malleable32_size(7, 6)); + BOOST_REQUIRE(!accessor::is_malleable32_size(7, 7)); + + BOOST_REQUIRE(!accessor::is_malleable32_size(8, 1)); + BOOST_REQUIRE(!accessor::is_malleable32_size(8, 2)); + BOOST_REQUIRE(!accessor::is_malleable32_size(8, 3)); + BOOST_REQUIRE(!accessor::is_malleable32_size(8, 4)); + BOOST_REQUIRE(!accessor::is_malleable32_size(8, 5)); + BOOST_REQUIRE(!accessor::is_malleable32_size(8, 6)); + BOOST_REQUIRE(!accessor::is_malleable32_size(8, 7)); + BOOST_REQUIRE(!accessor::is_malleable32_size(8, 8)); + + BOOST_REQUIRE(accessor::is_malleable32_size(9, 1)); // 10:1 + BOOST_REQUIRE(!accessor::is_malleable32_size(9, 2)); + BOOST_REQUIRE(!accessor::is_malleable32_size(9, 3)); + BOOST_REQUIRE(!accessor::is_malleable32_size(9, 4)); + BOOST_REQUIRE(!accessor::is_malleable32_size(9, 5)); + BOOST_REQUIRE(!accessor::is_malleable32_size(9, 6)); + BOOST_REQUIRE(!accessor::is_malleable32_size(9, 7)); + BOOST_REQUIRE(!accessor::is_malleable32_size(9, 8)); + BOOST_REQUIRE(!accessor::is_malleable32_size(9, 9)); + + BOOST_REQUIRE(!accessor::is_malleable32_size(10, 1)); + BOOST_REQUIRE(accessor::is_malleable32_size(10, 2)); // 12:2 + BOOST_REQUIRE(!accessor::is_malleable32_size(10, 3)); + BOOST_REQUIRE(accessor::is_malleable32_size(11, 1)); // 12:1 + BOOST_REQUIRE(!accessor::is_malleable32_size(11, 2)); + BOOST_REQUIRE(!accessor::is_malleable32_size(12, 1)); + BOOST_REQUIRE(!accessor::is_malleable32_size(12, 2)); + BOOST_REQUIRE(!accessor::is_malleable32_size(12, 3)); + BOOST_REQUIRE(accessor::is_malleable32_size(12, 4)); // 16:4 + BOOST_REQUIRE(accessor::is_malleable32_size(13, 1)); // 14:1 + BOOST_REQUIRE(!accessor::is_malleable32_size(13, 2)); + BOOST_REQUIRE(!accessor::is_malleable32_size(14, 1)); + BOOST_REQUIRE(accessor::is_malleable32_size(14, 2)); // 16:2 + BOOST_REQUIRE(!accessor::is_malleable32_size(14, 3)); + BOOST_REQUIRE(accessor::is_malleable32_size(15, 1)); // 16:1 + BOOST_REQUIRE(!accessor::is_malleable32_size(16, 1)); + BOOST_REQUIRE(accessor::is_malleable32_size(17, 1)); // 18:1 + BOOST_REQUIRE(!accessor::is_malleable32_size(18, 1)); + BOOST_REQUIRE(accessor::is_malleable32_size(18, 2)); // 20:2 + BOOST_REQUIRE(accessor::is_malleable32_size(19, 1)); // 20:1 + BOOST_REQUIRE(!accessor::is_malleable32_size(20, 1)); + BOOST_REQUIRE(!accessor::is_malleable32_size(20, 2)); + BOOST_REQUIRE(!accessor::is_malleable32_size(20, 3)); + BOOST_REQUIRE(accessor::is_malleable32_size(20, 4)); // 24:4 + BOOST_REQUIRE(accessor::is_malleable32_size(21, 1)); // 22:1 + BOOST_REQUIRE(!accessor::is_malleable32_size(22, 1)); + BOOST_REQUIRE(accessor::is_malleable32_size(22, 2)); // 24:2 + BOOST_REQUIRE(accessor::is_malleable32_size(23, 1)); // 24:1 + BOOST_REQUIRE(!accessor::is_malleable32_size(24, 1)); + BOOST_REQUIRE(accessor::is_malleable32_size(25, 1)); // 26:1 + BOOST_REQUIRE(!accessor::is_malleable32_size(26, 1)); + BOOST_REQUIRE(accessor::is_malleable32_size(26, 2)); // 28:2 + BOOST_REQUIRE(accessor::is_malleable32_size(27, 1)); // 28:1 + BOOST_REQUIRE(!accessor::is_malleable32_size(28, 1)); + BOOST_REQUIRE(!accessor::is_malleable32_size(28, 2)); + BOOST_REQUIRE(!accessor::is_malleable32_size(28, 3)); + BOOST_REQUIRE(accessor::is_malleable32_size(28, 4)); // 32:4 + BOOST_REQUIRE(accessor::is_malleable32_size(29, 1)); // 30:1 + BOOST_REQUIRE(!accessor::is_malleable32_size(30, 1)); + BOOST_REQUIRE(accessor::is_malleable32_size(30, 2)); // 32:2 + BOOST_REQUIRE(accessor::is_malleable32_size(31, 1)); // 32:1 +} + +BOOST_AUTO_TEST_SUITE_END()