diff --git a/Makefile.am b/Makefile.am
index f6d86b09d9..af80e31135 100755
--- a/Makefile.am
+++ b/Makefile.am
@@ -362,6 +362,8 @@ test_libbitcoin_system_test_SOURCES = \
test/unicode/utf8_everywhere/ofstream.cpp \
test/unicode/utf8_everywhere/unicode_istream.cpp \
test/unicode/utf8_everywhere/unicode_ostream.cpp \
+ test/utreexo/utreexo.cpp \
+ test/utreexo/utreexo.hpp \
test/wallet/context.cpp \
test/wallet/message.cpp \
test/wallet/neutrino_filter.cpp \
diff --git a/builds/cmake/CMakeLists.txt b/builds/cmake/CMakeLists.txt
index 4ebdf5b59b..430871bfa6 100644
--- a/builds/cmake/CMakeLists.txt
+++ b/builds/cmake/CMakeLists.txt
@@ -849,6 +849,8 @@ if (with-tests)
"../../test/unicode/utf8_everywhere/ofstream.cpp"
"../../test/unicode/utf8_everywhere/unicode_istream.cpp"
"../../test/unicode/utf8_everywhere/unicode_ostream.cpp"
+ "../../test/utreexo/utreexo.cpp"
+ "../../test/utreexo/utreexo.hpp"
"../../test/wallet/context.cpp"
"../../test/wallet/message.cpp"
"../../test/wallet/neutrino_filter.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 4a8715565d..b8f7c72dee 100644
--- a/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj
+++ b/builds/msvc/vs2022/libbitcoin-system-test/libbitcoin-system-test.vcxproj
@@ -245,6 +245,7 @@
+
@@ -305,6 +306,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 172500f9f4..42f272295f 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
@@ -14,7 +14,7 @@
{51A424A9-2C12-4211-0000-000000000001}
- {51A424A9-2C12-4211-0000-000000000002}
+ {51A424A9-2C12-4211-0000-000000000003}
{51A424A9-2C12-4211-0000-000000000002}
@@ -35,22 +35,22 @@
{51A424A9-2C12-4211-0000-000000000007}
- {51A424A9-2C12-4211-0000-000000000003}
+ {51A424A9-2C12-4211-0000-000000000004}
- {51A424A9-2C12-4211-0000-000000000006}
+ {51A424A9-2C12-4211-0000-000000000007}
- {51A424A9-2C12-4211-0000-000000000004}
+ {51A424A9-2C12-4211-0000-000000000005}
- {51A424A9-2C12-4211-0000-000000000005}
+ {51A424A9-2C12-4211-0000-000000000006}
{51A424A9-2C12-4211-0000-000000000008}
- {51A424A9-2C12-4211-0000-000000000007}
+ {51A424A9-2C12-4211-0000-000000000008}
{51A424A9-2C12-4211-0000-000000000009}
@@ -68,37 +68,40 @@
{51A424A9-2C12-4211-0000-00000000000D}
- {51A424A9-2C12-4211-0000-000000000008}
+ {51A424A9-2C12-4211-0000-000000000009}
- {51A424A9-2C12-4211-0000-000000000009}
+ {51A424A9-2C12-4211-0000-000000000010}
- {51A424A9-2C12-4211-0000-000000000010}
+ {51A424A9-2C12-4211-0000-0000000000A1}
{51A424A9-2C12-4211-0000-00000000000E}
- {51A424A9-2C12-4211-0000-0000000000A1}
+ {51A424A9-2C12-4211-0000-0000000000B1}
-
+
{51A424A9-2C12-4211-0000-00000000000F}
+
+ {51A424A9-2C12-4211-0000-000000000001}
+
- {51A424A9-2C12-4211-0000-0000000000B1}
+ {51A424A9-2C12-4211-0000-0000000000C1}
- {51A424A9-2C12-4211-0000-0000000000C1}
+ {51A424A9-2C12-4211-0000-0000000000D1}
- {51A424A9-2C12-4211-0000-0000000000D1}
+ {51A424A9-2C12-4211-0000-0000000000E1}
- {51A424A9-2C12-4211-0000-000000000001}
+ {51A424A9-2C12-4211-0000-000000000002}
- {51A424A9-2C12-4211-0000-0000000000E1}
+ {51A424A9-2C12-4211-0000-0000000000F1}
@@ -561,6 +564,9 @@
src\unicode\utf8_everywhere
+
+ src\utreexo
+
src\wallet\addresses
@@ -695,6 +701,9 @@
src
+
+ src\utreexo
+
src\wallet\mnemonics
diff --git a/include/bitcoin/system/impl/math/bits.ipp b/include/bitcoin/system/impl/math/bits.ipp
index b6cb4db7f4..26afe1fb65 100644
--- a/include/bitcoin/system/impl/math/bits.ipp
+++ b/include/bitcoin/system/impl/math/bits.ipp
@@ -294,8 +294,8 @@ template >
constexpr void shift_left_into(Value& value, size_t shift, bool overflow) NOEXCEPT
{
constexpr auto span = bits;
- overflow && shift >= span ? value = 0 :
- value <<= (shift % span);
+ overflow && shift >= span ? value = 0 : depromote(
+ value <<= (shift % span));
}
// signed overloads (shift left of negative is undefined behavior).
diff --git a/include/bitcoin/system/impl/math/overflow.ipp b/include/bitcoin/system/impl/math/overflow.ipp
index d2429e0b3a..d4487ded29 100644
--- a/include/bitcoin/system/impl/math/overflow.ipp
+++ b/include/bitcoin/system/impl/math/overflow.ipp
@@ -33,6 +33,21 @@ constexpr Integral minimum_ = std::numeric_limits::min();
template = true>
constexpr Integral maximum_ = std::numeric_limits::max();
+// shift
+// ----------------------------------------------------------------------------
+
+template >
+constexpr bool is_left_shift_overflow(Value value, size_t shift) NOEXCEPT
+{
+ return to_bool(bit_and(value, unmask_left(shift)));
+}
+
+template >
+constexpr bool is_right_shift_overflow(Value value, size_t shift) NOEXCEPT
+{
+ return to_bool(bit_and(value, unmask_right(shift)));
+}
+
// add/subtract
// ----------------------------------------------------------------------------
diff --git a/include/bitcoin/system/math/overflow.hpp b/include/bitcoin/system/math/overflow.hpp
index 9dd9870b14..7503c0f8ac 100644
--- a/include/bitcoin/system/math/overflow.hpp
+++ b/include/bitcoin/system/math/overflow.hpp
@@ -23,8 +23,19 @@
namespace libbitcoin {
namespace system {
+
+/// shift
+/// ---------------------------------------------------------------------------
+
+/// Shift would cause 1-valued bit(s) to be left-shifted out.
+template = true>
+constexpr bool is_left_shift_overflow(Value value, size_t shift) NOEXCEPT;
+
+/// Shift would cause 1-valued bit(s) to be right-shifted out.
+template = true>
+constexpr bool is_right_shift_overflow(Value value, size_t shift) NOEXCEPT;
-/// add/subtract.
+/// shift/add/subtract.
/// ---------------------------------------------------------------------------
// TODO: generalize is_add_overflow() and is_subtract_overflow() argument types.
diff --git a/test/math/overflow.cpp b/test/math/overflow.cpp
index 2c93aa7f4b..ffa24447eb 100644
--- a/test/math/overflow.cpp
+++ b/test/math/overflow.cpp
@@ -30,6 +30,67 @@ constexpr auto signed_zero = 0_i16;
constexpr auto signed_max = max_int16;
constexpr auto signed_half = to_half(signed_max);
+// is_left_shift_overflow
+// ----------------------------------------------------------------------------
+
+// unsigned only
+static_assert(!is_left_shift_overflow(0b00000000, 0));
+static_assert(!is_left_shift_overflow(0b00000000, 1));
+static_assert(!is_left_shift_overflow(0b00000000, 2));
+static_assert(!is_left_shift_overflow(0b00000000, 4));
+static_assert(!is_left_shift_overflow(0b00000000, 8));
+
+static_assert(!is_left_shift_overflow(0b00000001, 0));
+static_assert(!is_left_shift_overflow(0b00000001, 1));
+static_assert(!is_left_shift_overflow(0b00000001, 2));
+static_assert(!is_left_shift_overflow(0b00000001, 4));
+static_assert( is_left_shift_overflow(0b00000001, 8));
+
+static_assert(!is_left_shift_overflow(0b10000000, 0));
+static_assert( is_left_shift_overflow(0b10000000, 1));
+static_assert( is_left_shift_overflow(0b10000000, 2));
+static_assert( is_left_shift_overflow(0b10000000, 4));
+static_assert( is_left_shift_overflow(0b10000000, 8));
+
+static_assert(!is_left_shift_overflow(0b00101010, 0));
+static_assert(!is_left_shift_overflow(0b00101010, 1));
+static_assert(!is_left_shift_overflow(0b00101010, 2));
+static_assert( is_left_shift_overflow(0b00101010, 4));
+static_assert( is_left_shift_overflow(0b00101010, 8));
+
+static_assert(!is_left_shift_overflow(0b11111111, 0));
+static_assert( is_left_shift_overflow(0b11111111, 1));
+static_assert( is_left_shift_overflow(0b11111111, 2));
+static_assert( is_left_shift_overflow(0b11111111, 4));
+static_assert( is_left_shift_overflow(0b11111111, 8));
+
+static_assert(!is_left_shift_overflow(0b00010000'00000000, 0));
+static_assert(!is_left_shift_overflow(0b00010000'00000000, 1));
+static_assert(!is_left_shift_overflow(0b00010000'00000000, 2));
+static_assert( is_left_shift_overflow(0b00010000'00000000, 4));
+static_assert( is_left_shift_overflow(0b00010000'00000000, 8));
+static_assert( is_left_shift_overflow(0b00000000'00000001, bits));
+static_assert(!is_left_shift_overflow(0b00000000'00000001, sub1(bits)));
+
+static_assert(!is_left_shift_overflow(0b00010000'00000000'00000000'00000000, 0));
+static_assert(!is_left_shift_overflow(0b00010000'00000000'00000000'00000000, 1));
+static_assert(!is_left_shift_overflow(0b00010000'00000000'00000000'00000000, 2));
+static_assert( is_left_shift_overflow(0b00010000'00000000'00000000'00000000, 4));
+static_assert( is_left_shift_overflow(0b00010000'00000000'00000000'00000000, 8));
+static_assert( is_left_shift_overflow(0b00000000'00000000'00000000'00000001, bits));
+static_assert(!is_left_shift_overflow(0b00000000'00000000'00000000'00000001, sub1(bits)));
+
+static_assert(!is_left_shift_overflow(0b00010000'00000000'00000000'00000000'00010000'00000000'00000000'00000000, 0));
+static_assert(!is_left_shift_overflow(0b00010000'00000000'00000000'00000000'00010000'00000000'00000000'00000000, 1));
+static_assert(!is_left_shift_overflow(0b00010000'00000000'00000000'00000000'00010000'00000000'00000000'00000000, 2));
+static_assert( is_left_shift_overflow(0b00010000'00000000'00000000'00000000'00010000'00000000'00000000'00000000, 4));
+static_assert( is_left_shift_overflow(0b00010000'00000000'00000000'00000000'00010000'00000000'00000000'00000000, 8));
+static_assert( is_left_shift_overflow(0b00000000'00000000'00000000'00000000'00000000'00000000'00000000'00000001, bits));
+static_assert(!is_left_shift_overflow(0b00000000'00000000'00000000'00000000'00000000'00000000'00000000'00000001, sub1(bits)));
+
+// is_right_shift_overflow
+// ----------------------------------------------------------------------------
+
// is_negate_overflow
// ----------------------------------------------------------------------------
@@ -68,7 +129,7 @@ static_assert(is_add_overflow(unsigned_max, unsigned_half));
static_assert(is_add_overflow(unsigned_half, unsigned_max));
static_assert(!is_add_overflow(unsigned_half, unsigned_half));
-// is_underflow
+// is_subtract_overflow
// ----------------------------------------------------------------------------
// signed
diff --git a/test/utreexo/utreexo.cpp b/test/utreexo/utreexo.cpp
index b5da3e7aaa..dadecf0c51 100644
--- a/test/utreexo/utreexo.cpp
+++ b/test/utreexo/utreexo.cpp
@@ -25,6 +25,67 @@ BOOST_AUTO_TEST_SUITE(utreexo_tests)
// inferred from rustreexo implementation (no test provided)
+BOOST_AUTO_TEST_CASE(utreexo__parent__various__expected)
+{
+ static_assert(parent(0, 0) == 1);
+ static_assert(parent(0, 1) == 2);
+ static_assert(parent(0, 2) == 4);
+ static_assert(parent(0, 3) == 8);
+ static_assert(parent(0, 4) == 16);
+ static_assert(parent(0, 5) == 32);
+ static_assert(parent(0, 6) == 64);
+
+ static_assert(parent(1, 0) == 1);
+ static_assert(parent(1, 1) == 2);
+ static_assert(parent(1, 2) == 4);
+ static_assert(parent(1, 3) == 8);
+ static_assert(parent(1, 4) == 16);
+ static_assert(parent(1, 5) == 32);
+ static_assert(parent(1, 6) == 64);
+
+ static_assert(parent(128, 0) == 64 + 1);
+ static_assert(parent(128, 1) == 64 + 2);
+ static_assert(parent(128, 2) == 64 + 4);
+ static_assert(parent(128, 3) == 64 + 8);
+ static_assert(parent(128, 4) == 64 + 16);
+ static_assert(parent(128, 5) == 64 + 32);
+ static_assert(parent(128, 6) == 64);
+
+ BOOST_REQUIRE_EQUAL(parent(128, 0), 65_u64);
+ BOOST_REQUIRE_EQUAL(parent(128, 1), 66_u64);
+ BOOST_REQUIRE_EQUAL(parent(128, 2), 68_u64);
+ BOOST_REQUIRE_EQUAL(parent(128, 3), 72_u64);
+ BOOST_REQUIRE_EQUAL(parent(128, 4), 80_u64);
+ BOOST_REQUIRE_EQUAL(parent(128, 5), 96_u64);
+ BOOST_REQUIRE_EQUAL(parent(128, 6), 64_u64);
+}
+
+BOOST_AUTO_TEST_CASE(utreexo__left_child__various__expected)
+{
+ // same as children()
+ static_assert(left_child(4, 2) == 0);
+ static_assert(left_child(49, 5) == 34);
+ static_assert(left_child(50, 5) == 36);
+ static_assert(left_child(44, 5) == 24);
+ BOOST_REQUIRE_EQUAL(left_child(4, 2), 0_u64);
+ BOOST_REQUIRE_EQUAL(left_child(49, 5), 34_u64);
+ BOOST_REQUIRE_EQUAL(left_child(50, 5), 36_u64);
+ BOOST_REQUIRE_EQUAL(left_child(44, 5), 24_u64);
+}
+
+BOOST_AUTO_TEST_CASE(utreexo__right_child__various__expected)
+{
+ // same as add1(children())
+ static_assert(right_child(4, 2) == 0 + 1);
+ static_assert(right_child(49, 5) == 34 + 1);
+ static_assert(right_child(50, 5) == 36 + 1);
+ static_assert(right_child(44, 5) == 24 + 1);
+ BOOST_REQUIRE_EQUAL(right_child(4, 2), 1_u64);
+ BOOST_REQUIRE_EQUAL(right_child(49, 5), 35_u64);
+ BOOST_REQUIRE_EQUAL(right_child(50, 5), 37_u64);
+ BOOST_REQUIRE_EQUAL(right_child(44, 5), 25_u64);
+}
+
BOOST_AUTO_TEST_CASE(utreexo__is_root_populated__various__expected)
{
static_assert(!is_root_populated(0b00000000, 0));
@@ -66,14 +127,14 @@ BOOST_AUTO_TEST_CASE(utreexo__left_sibling__various__expected)
BOOST_AUTO_TEST_CASE(utreexo__start_position_at_row__various__expected)
{
// forest_rows must be >= row.
- static_assert(start_position_at_row(0, 0) == 0_u64);
- static_assert(start_position_at_row(0, 1) == 0_u64);
- static_assert(start_position_at_row(1, 1) == 2_u64);
- static_assert(start_position_at_row(1, 2) == 4_u64);
- static_assert(start_position_at_row(1, 3) == 8_u64);
- static_assert(start_position_at_row(2, 2) == 6_u64);
- static_assert(start_position_at_row(2, 3) == 12_u64);
- static_assert(start_position_at_row(3, 3) == 14_u64);
+ static_assert(start_position_at_row(0, 0) == 0);
+ static_assert(start_position_at_row(0, 1) == 0);
+ static_assert(start_position_at_row(1, 1) == 2);
+ static_assert(start_position_at_row(1, 2) == 4);
+ static_assert(start_position_at_row(1, 3) == 8);
+ static_assert(start_position_at_row(2, 2) == 6);
+ static_assert(start_position_at_row(2, 3) == 12);
+ static_assert(start_position_at_row(3, 3) == 14);
BOOST_REQUIRE_EQUAL(start_position_at_row(0, 0), 0_u64);
BOOST_REQUIRE_EQUAL(start_position_at_row(0, 1), 0_u64);
BOOST_REQUIRE_EQUAL(start_position_at_row(1, 1), 2_u64);
@@ -86,11 +147,11 @@ BOOST_AUTO_TEST_CASE(utreexo__start_position_at_row__various__expected)
BOOST_AUTO_TEST_CASE(utreexo__number_of_roots__various__expected)
{
- static_assert(number_of_roots(0) == 0_size);
- static_assert(number_of_roots(1) == 1_size);
- static_assert(number_of_roots(2) == 1_size);
- static_assert(number_of_roots(3) == 2_size);
- static_assert(number_of_roots(0xfefefefefefefefe) == (64_size - 8_size));
+ static_assert(number_of_roots(0) == 0);
+ static_assert(number_of_roots(1) == 1);
+ static_assert(number_of_roots(2) == 1);
+ static_assert(number_of_roots(3) == 2);
+ static_assert(number_of_roots(0xfefefefefefefefe) == (64 - 8));
BOOST_REQUIRE_EQUAL(number_of_roots(0), 0_size);
BOOST_REQUIRE_EQUAL(number_of_roots(1), 1_size);
BOOST_REQUIRE_EQUAL(number_of_roots(2), 1_size);
@@ -131,10 +192,10 @@ BOOST_AUTO_TEST_CASE(utreexo__is_sibling__various__expected)
// github.com/mit-dci/rustreexo/blob/main/src/accumulator/util.rs#L474
BOOST_AUTO_TEST_CASE(utreexo__children__various__expected)
{
- static_assert(children(4, 2) == 0_u64);
- static_assert(children(49, 5) == 34_u64);
- static_assert(children(50, 5) == 36_u64);
- static_assert(children(44, 5) == 24_u64);
+ static_assert(children(4, 2) == 0);
+ static_assert(children(49, 5) == 34);
+ static_assert(children(50, 5) == 36);
+ static_assert(children(44, 5) == 24);
BOOST_REQUIRE_EQUAL(children(4, 2), 0_u64);
BOOST_REQUIRE_EQUAL(children(49, 5), 34_u64);
BOOST_REQUIRE_EQUAL(children(50, 5), 36_u64);
@@ -151,10 +212,10 @@ BOOST_AUTO_TEST_CASE(utreexo__is_root_position__various__expected)
// github.com/mit-dci/rustreexo/blob/main/src/accumulator/util.rs#L424
BOOST_AUTO_TEST_CASE(utreexo__tree_rows__various__expected)
{
- static_assert(tree_rows(8) == 3_u8);
- static_assert(tree_rows(9) == 4_u8);
- static_assert(tree_rows(12) == 4_u8);
- static_assert(tree_rows(255) == 8_u8);
+ static_assert(tree_rows(8) == 3);
+ static_assert(tree_rows(9) == 4);
+ static_assert(tree_rows(12) == 4);
+ static_assert(tree_rows(255) == 8);
BOOST_REQUIRE_EQUAL(tree_rows(8), 3_u8);
BOOST_REQUIRE_EQUAL(tree_rows(9), 4_u8);
BOOST_REQUIRE_EQUAL(tree_rows(12), 4_u8);
@@ -192,15 +253,17 @@ BOOST_AUTO_TEST_CASE(utreexo__detect_row__scenario__expected)
return success;
};
- static_assert(test_detect_row_());
+
+ // constexpr evaluation hit maximum step limit.
+ ////static_assert(test_detect_row_());
BOOST_REQUIRE(test_detect_row_());
}
// github.com/mit-dci/rustreexo/blob/main/src/accumulator/util.rs#L359
BOOST_AUTO_TEST_CASE(utreexo__root_position__various__expected)
{
- static_assert(root_position(5, 2, 3) == 12_u64);
- static_assert(root_position(5, 0, 3) == 4_u64);
+ static_assert(root_position(5, 2, 3) == 12);
+ static_assert(root_position(5, 0, 3) == 4);
BOOST_REQUIRE_EQUAL(root_position(5, 2, 3), 12_u64);
BOOST_REQUIRE_EQUAL(root_position(5, 0, 3), 4_u64);
}
@@ -246,14 +309,30 @@ BOOST_AUTO_TEST_CASE(utreexo__detwin__various__expected)
// 0 1 2 3 4 5 6 7
const positions expected1{ 7, 8, 10 };
- positions targets1{ 0, 1, 4, 5, 7 };
- detwin(targets1, 3);
- BOOST_REQUIRE_EQUAL(targets1, expected1);
+ const positions targets1{ 0, 1, 4, 5, 7 };
+ BOOST_REQUIRE_EQUAL(detwin(targets1, 3), expected1);
const positions expected2{ 4, 6, 12 };
- positions targets2{ 4, 6, 8, 9 };
- detwin(targets2, 3);
- BOOST_REQUIRE_EQUAL(targets2, expected2);
+ const positions targets2{ 4, 6, 8, 9 };
+ BOOST_REQUIRE_EQUAL(detwin(targets2, 3), expected2);
+}
+
+BOOST_AUTO_TEST_CASE(utreexo__get_proof_positions__sorted__expected)
+{
+ const positions expected{ 6, 9 };
+ constexpr uint64_t leaves = 8;
+ constexpr auto rows = tree_rows(leaves);
+ const auto targets = get_proof_positions({ 4, 5, 7, 8 }, leaves, rows);
+ BOOST_REQUIRE_EQUAL(targets, expected);
+}
+
+BOOST_AUTO_TEST_CASE(utreexo__get_proof_positions__unsorted__expected)
+{
+ const positions expected{ 6, 9 };
+ constexpr uint64_t leaves = 8;
+ constexpr auto rows = tree_rows(leaves);
+ const auto targets = get_proof_positions({ 4, 8, 7, 5 }, leaves, rows);
+ BOOST_REQUIRE_EQUAL(targets, expected);
}
// rustreexo examples
diff --git a/test/utreexo/utreexo.hpp b/test/utreexo/utreexo.hpp
index 8a1ab130c5..c493f0c93f 100644
--- a/test/utreexo/utreexo.hpp
+++ b/test/utreexo/utreexo.hpp
@@ -20,21 +20,18 @@
#define LIBBITCOIN_SYSTEM_UTREEXO_HPP
#include
+#include
#include
#include
namespace libbitcoin {
namespace system {
namespace utreexo {
-
-using node_hash = hash_digest;
+
using positions = std::vector;
+using node_hash = hash_digest;
constexpr auto empty_hash = node_hash{};
-
-constexpr std::pair okay(bool result) NOEXCEPT
-{
- return { result, (result ? "success" : "fail") };
-}
+constexpr auto dummy_hash = base16_hash("4242424242424242424242424242424242424242424242424242424242424242");
constexpr node_hash parent_hash(const node_hash& left,
const node_hash& right) NOEXCEPT
@@ -47,41 +44,41 @@ constexpr node_hash hash_from_u8(uint8_t byte) NOEXCEPT
return sha256::hash(byte);
}
-// TODO: test.
-// return parent position of passed-in child
-constexpr uint64_t parent(uint64_t position, uint8_t forest_rows) NOEXCEPT
+constexpr uint64_t parent(uint64_t child, uint8_t forest_rows) NOEXCEPT
{
- return set_right(shift_right(position), forest_rows);
+ return set_right(shift_right(child), forest_rows);
}
-constexpr uint64_t children(uint64_t position, uint8_t forest_rows) NOEXCEPT
+constexpr uint64_t children(uint64_t parent, uint8_t forest_rows) NOEXCEPT
{
- const auto mask = unmask_right(add1(forest_rows));
- return bit_and(shift_left(position), mask);
+ // What happens when these bits are lost?
+ BC_ASSERT(!is_left_shift_overflow(parent, add1(forest_rows)));
+
+ return bit_and(shift_left(parent),
+ unmask_right(add1(forest_rows)));
}
-// TODO: test.
-constexpr uint64_t left_child(uint64_t position, uint8_t forest_rows) NOEXCEPT
+constexpr uint64_t left_child(uint64_t parent, uint8_t forest_rows) NOEXCEPT
{
- return children(position, forest_rows);
+ return children(parent, forest_rows);
}
-// TODO: test.
-constexpr uint64_t right_child(uint64_t position, uint8_t forest_rows) NOEXCEPT
+constexpr uint64_t right_child(uint64_t parent, uint8_t forest_rows) NOEXCEPT
{
- BC_ASSERT(!is_add_overflow(children(position, forest_rows), one));
+ // What happens when these bits are lost?
+ BC_ASSERT(!is_add_overflow(children(parent, forest_rows), one));
- return add1(children(position, forest_rows));
+ return add1(children(parent, forest_rows));
}
-constexpr uint64_t left_sibling(uint64_t position) NOEXCEPT
+constexpr uint64_t left_sibling(uint64_t node) NOEXCEPT
{
- return set_right(position, zero, false);
+ return set_right(node, zero, false);
}
-constexpr bool is_left_niece(uint64_t position) NOEXCEPT
+constexpr bool is_left_niece(uint64_t node) NOEXCEPT
{
- return !get_right(position);
+ return !get_right(node);
}
constexpr bool is_right_sibling(uint64_t node, uint64_t next) NOEXCEPT
@@ -100,25 +97,22 @@ constexpr bool is_root_populated(uint64_t leaves, uint8_t row) NOEXCEPT
return get_right(leaves, row);
}
-constexpr uint8_t detect_row(uint64_t position, uint8_t forest_rows) NOEXCEPT
+constexpr uint8_t detect_row(uint64_t node, uint8_t forest_rows) NOEXCEPT
{
auto bit = forest_rows;
- while (!is_zero(bit) && get_right(position, bit)) --bit;
+ while (!is_zero(bit) && get_right(node, bit)) { --bit; }
return subtract(forest_rows, bit);
}
+// TODO: arbitrary behavior if given row does not have a root.
constexpr uint64_t root_position(uint64_t leaves, uint8_t row,
uint8_t forest_rows) NOEXCEPT
{
- // TODO: undefined behavior if given row does not have a root.
- BC_ASSERT(!is_add_overflow(row, 1));
- BC_ASSERT(!is_add_overflow(forest_rows, 1));
- BC_ASSERT(!is_subtract_overflow(add1(forest_rows), row));
-
- // sub1 cannot overflow here.
- const auto mask = unmask_right(add1(forest_rows));
- const auto before = bit_and(leaves, shift_left(mask, add1(row)));
- const auto left = shift_left(mask, subtract(add1(forest_rows), row));
+ BC_ASSERT(!is_subtract_overflow(add1(forest_rows), row));
+
+ const auto mask = unmask_right(add1(forest_rows));
+ const auto before = bit_and(leaves, shift_left(mask, add1(row)));
+ const auto left = shift_left(mask, subtract(add1(forest_rows), row));
const auto right = shift_right(before, row);
const auto shifted = bit_or(left, right);
return bit_and(shifted, mask);
@@ -128,31 +122,29 @@ constexpr bool is_root_position(uint64_t position, uint64_t leaves,
uint8_t forest_rows) NOEXCEPT
{
const auto row = detect_row(position, forest_rows);
- const auto present = get_right(leaves, row);
- const auto expected = root_position(leaves, row, forest_rows);
- return present && (position == expected);
+ return get_right(leaves, row) &&
+ (position == root_position(leaves, row, forest_rows));
}
constexpr uint64_t remove_bit(uint64_t value, size_t bit) NOEXCEPT
{
- const auto mask_lo = mask_right(add1(bit));
- const auto mask_hi = unmask_right(bit);
- return bit_or(shift_right(bit_and(value, mask_lo), 1),
- bit_and(value, mask_hi));
+ const auto hi = unmask_right(bit);
+ const auto lo = mask_right(add1(bit));
+ return bit_or(shift_right(bit_and(value, lo)), bit_and(value, hi));
}
-constexpr bool calculate_next(uint64_t& out, uint64_t position,
- uint64_t delete_position, uint8_t forest_rows) NOEXCEPT
+constexpr bool calculate_next(uint64_t& out, uint64_t node,
+ uint64_t delete_node, uint8_t forest_rows) NOEXCEPT
{
- const auto position_row = detect_row(position, forest_rows);
- const auto delete_row = detect_row(delete_position, forest_rows);
- if (delete_row < position_row)
+ const auto node_row = detect_row(node, forest_rows);
+ const auto delete_row = detect_row(delete_node, forest_rows);
+ if (is_subtract_overflow(delete_row, node_row))
return false;
- const auto bit = subtract(delete_row, position_row);
- const auto lo = remove_bit(position, bit);
+ const auto bit = subtract(delete_row, node_row);
+ const auto lo = remove_bit(node, bit);
- const auto row = add1(position_row);
+ const auto row = add1(node_row);
const auto hi = shift_left(bit_right(row),
subtract(forest_rows, row));
@@ -178,31 +170,173 @@ constexpr size_t number_of_roots(uint64_t leaves) NOEXCEPT
constexpr uint8_t tree_rows(uint64_t leaves) NOEXCEPT
{
- // nothing here can overflow.
return narrow_cast(is_zero(leaves) ? zero :
subtract(bits, left_zeros(sub1(leaves))));
}
-inline void detwin(positions& nodes, uint8_t forest_rows) NOEXCEPT
+inline positions detwin(const positions& nodes, uint8_t forest_rows) NOEXCEPT
{
if (nodes.empty())
- return;
+ return {};
+
+ auto out{ nodes };
+ for (auto index{ one }; index < out.size(); ++index)
+ {
+ const auto node = out.at(sub1(index));
+ const auto next = out.at(index);
+
+ if (is_right_sibling(node, next))
+ {
+ const auto dad = parent(node, forest_rows);
+ const auto from = std::next(out.begin(), sub1(index));
+ const auto stop = std::next(out.begin(), add1(index));
+ out.erase(from, stop);
+ out.push_back(dad);
+ sort(out);
+ --index;
+ }
+ }
+
+ return out;
+}
+
+// TODO: test.
+// TODO: guard overflows.
+bool roots_to_destroy(positions& out, std::vector&& roots,
+ uint64_t adding, uint64_t leaves) NOEXCEPT
+{
+ uint8_t row{};
+
+ for (auto leaf{ adding }; !is_zero(leaf); --leaf, ++leaves)
+ {
+ while (get_right(leaves, add1(row)))
+ {
+ if (roots.empty())
+ return false;
+
+ if (roots.back() == empty_hash)
+ {
+ const auto rows = tree_rows(add(leaves, leaf));
+ out.push_back(root_position(leaves, row, rows));
+ }
+
+ roots.pop_back();
+ ++row;
+ }
+
+ roots.push_back(dummy_hash);
+ }
+
+ return true;
+}
+
+// TODO: test.
+// TODO: guard overflows.
+std::tuple detect_offset(uint64_t node,
+ uint64_t leaves) NOEXCEPT
+{
+ uint8_t trees{};
+ auto rows = tree_rows(leaves);
+ const auto row = detect_row(node, rows);
- for (auto node = std::next(nodes.begin()); node != nodes.end();)
+ while (true)
{
- const auto prior = std::prev(node);
+ const auto mask = unmask_right(rows);
+ const auto size = bit_and(set_right(rows), leaves);
+ if (bit_and(shift_left(node, row), mask) < size)
+ break;
- if (is_right_sibling(*prior, *node))
+ if (!is_zero(size))
{
- const auto dad = parent(*prior, forest_rows);
- node = nodes.erase(prior, std::next(node));
- nodes.push_back(dad);
- sort(nodes);
- continue;
+ node -= size;
+ ++trees;
}
- ++node;
+ --rows;
+ };
+
+ return { trees, subtract(rows, row), !node };
+}
+
+// TODO: test.
+constexpr bool parent_many(uint64_t& out, uint64_t node, uint8_t rise,
+ uint8_t forest_rows) NOEXCEPT
+{
+ if (is_zero(rise))
+ {
+ out = node;
+ return true;
+ }
+
+ if (rise > forest_rows)
+ return false;
+
+ const auto left = subtract(forest_rows, sub1(rise));
+ const auto mask = sub1(bit_right(add1(forest_rows)));
+ out = bit_and(bit_or(shift_right(node, rise), shift_left(mask, left)), mask);
+ return true;
+}
+
+// TODO: test.
+constexpr bool max_position_at_row(uint64_t& out, uint8_t row, uint8_t rows,
+ uint64_t leaves) NOEXCEPT
+{
+ uint64_t many{};
+ if (!parent_many(many, leaves, row, rows))
+ return false;
+
+ out = floored_subtract(many, one);
+ return true;
+}
+
+// TODO: test.
+constexpr bool is_ancestor(uint64_t higer, uint64_t lower,
+ uint8_t forest_rows) NOEXCEPT
+{
+ if (higer == lower)
+ return false;
+
+ uint64_t ancestor{};
+ const auto lo = detect_row(lower, forest_rows);
+ const auto hi = detect_row(higer, forest_rows);
+ return !is_subtract_overflow(hi, lo) &&
+ parent_many(ancestor, lower, subtract(hi, lo), forest_rows) &&
+ (ancestor == higer);
+}
+
+inline positions get_proof_positions(positions&& targets, uint64_t leaves,
+ uint8_t forest_rows) NOEXCEPT
+{
+ sort(targets);
+ positions proof{};
+
+ for (uint8_t row{}; row < forest_rows; ++row)
+ {
+ auto sorted{ true };
+ const auto rows{ targets };
+
+ for (auto it = rows.begin(); it != rows.end(); ++it)
+ {
+ const auto node = *it;
+ if ((detect_row(node, forest_rows) != row) ||
+ is_root_position(node, leaves, forest_rows))
+ continue;
+
+ const auto next = std::next(it);
+ if (next != rows.end() && is_sibling(node, *next))
+ ++it;
+ else
+ proof.push_back(bit_xor(node, one));
+
+ targets.push_back(parent(node, forest_rows));
+ sorted = false;
+ }
+
+ if (!sorted)
+ sort(targets);
}
+
+ return proof;
}
} // namespace utreexo
@@ -210,166 +344,3 @@ inline void detwin(positions& nodes, uint8_t forest_rows) NOEXCEPT
} // namespace libbitcoin
#endif
-
-////// roots_to_destroy returns the empty roots that get written over after num_adds
-////// amount of leaves have been added.
-////pub fn roots_to_destroy(
-//// num_adds: u64,
-//// mut num_leaves: u64,
-//// orig_roots: &[Hash],
-////) -> Vec {
-//// let mut roots = orig_roots.to_vec();
-//// let mut deleted = vec![];
-//// let mut h = 0;
-//// for add in 0..num_adds {
-//// while (num_leaves >> h) & 1 == 1 {
-//// let root = roots
-//// .pop()
-//// .expect("If (num_leaves >> h) & 1 == 1, it must have at least one root left");
-//// if root.is_empty() {
-//// let root_pos =
-//// root_position(num_leaves, h, tree_rows(num_leaves + (num_adds - add)));
-//// deleted.push(root_pos);
-//// }
-//// h += 1;
-//// }
-//// // Just adding a non-zero value to the slice.
-//// roots.push(AccumulatorHash::placeholder());
-//// num_leaves += 1;
-//// }
-////
-//// deleted
-////}
-
-////pub fn detect_offset(pos: u64, num_leaves: u64) -> (u8, u8, u64) {
-//// let mut tr = tree_rows(num_leaves);
-//// let nr = detect_row(pos, tr);
-////
-//// let mut bigger_trees: u8 = 0;
-//// let mut marker = pos;
-////
-//// // add trees until you would exceed position of node
-////
-//// // This is a bit of an ugly predicate. The goal is to detect if we've
-//// // gone past the node we're looking for by inspecting progressively shorter
-//// // trees; once we have, the loop is over.
-
-//// // The predicate breaks down into 3 main terms:
-//// // A: pos << nh
-//// // B: mask
-//// // C: 1<= C)
-//// // A is position up-shifted by the row of the node we're targeting.
-//// // B is the "mask" we use in other functions; a bunch of 0s at the MSB side
-//// // and then a bunch of 1s on the LSB side, such that we can use bitwise AND
-//// // to discard high bits. Together, A&B is shifting position up by nr bits,
-//// // and then discarding (zeroing out) the high bits. This is the same as in
-//// // n_grandchild. C checks for whether a tree exists at the current tree
-//// // rows. If there is no tree at tr, C is 0. If there is a tree, it will
-//// // return a power of 2: the base size of that tree.
-//// // The C term actually is used 3 times here, which is ugly; it's redefined
-//// // right on the next line.
-//// // In total, what this loop does is to take a node position, and
-//// // see if it's in the next largest tree. If not, then subtract everything
-//// // covered by that tree from the position, and proceed to the next tree,
-//// // skipping trees that don't exist.
-
-//// while (marker << nr) & ((2 << tr) - 1) >= (1 << tr) & num_leaves {
-//// let tree_size = (1 << tr) & num_leaves;
-//// if tree_size != 0 {
-//// marker -= tree_size;
-//// bigger_trees += 1;
-//// }
-//// tr -= 1;
-//// }
-////
-//// (bigger_trees, tr - nr, !marker)
-////}
-
-/////// max_position_at_row returns the biggest position an accumulator can have for the
-/////// requested row for the given num_leaves.
-////pub fn max_position_at_row(row: u8, total_rows: u8, num_leaves: u64) -> Result {
-//// Ok(parent_many(num_leaves, row, total_rows)?.saturating_sub(1))
-////}
-////
-////pub fn read_u64(buf: &mut Source) -> Result {
-//// let mut bytes = [0u8; 8];
-//// buf.read_exact(&mut bytes)
-//// .map_err(|_| "Failed to read u64")?;
-//// Ok(u64::from_le_bytes(bytes))
-////}
-
-////pub fn parent_many(pos: u64, rise: u8, forest_rows: u8) -> Result {
-//// if rise == 0 {
-//// return Ok(pos);
-//// }
-//// if rise > forest_rows {
-//// return Err(format!(
-//// "Cannot rise more than the forestRows: rise: {} forest_rows: {}",
-//// rise, forest_rows
-//// ));
-//// }
-////
-//// let mask = (2_u64 << forest_rows) - 1;
-//// Ok((pos >> rise | (mask << (forest_rows - (rise - 1)) as u64)) & mask)
-////}
-
-////std::pair is_ancestor(uint64_t higher_pos,
-//// uint64_t lower_pos, uint8_t forest_rows)
-////{
-//// if (higher_pos == lower_pos)
-//// return okay(false);
-////
-//// auto lower_row = detect_row(lower_pos, forest_rows);
-//// auto higher_row = detect_row(higher_pos, forest_rows);
-////
-//// // Prevent underflows by checking that the higherRow is not less than lowerRow.
-//// if (higher_row < lower_row)
-//// return okay(false);
-////
-//// // TODO: Return false if we error out or the calculated ancestor doesn't match the higherPos.
-//// auto ancestor = parent_many(lower_pos, higher_row - lower_row, forest_rows);
-////
-//// return (higher_pos == ancestor);
-////}
-
-/////// Returns which node should have its hashes on the proof, along with all nodes
-/////// whose hashes will be calculated to reach a root
-////Vec get_proof_positions(targets: &[u64], num_leaves: u64, forest_rows: u8) NOEXCEPT
-////{
-//// let mut proof_positions = vec![];
-//// let mut computed_positions = targets.to_vec();
-//// computed_positions.sort();
-////
-//// for row in 0..=forest_rows {
-//// let mut row_targets = computed_positions
-//// .iter()
-//// .cloned()
-//// .filter(|x| super::util::detect_row(*x, forest_rows) == row)
-//// .collect::>()
-//// .into_iter()
-//// .peekable();
-////
-//// while let Some(node) = row_targets.next() {
-//// if is_root_position(node, num_leaves, forest_rows) {
-//// continue;
-//// }
-////
-//// if let Some(next) = row_targets.peek() {
-//// if !is_sibling(node, *next) {
-//// proof_positions.push(node ^ 1);
-//// } else {
-//// row_targets.next();
-//// }
-//// } else {
-//// proof_positions.push(node ^ 1);
-//// }
-////
-//// computed_positions.push(parent(node, forest_rows));
-//// }
-////
-//// computed_positions.sort();
-//// }
-////
-//// proof_positions
-////}
|