Skip to content

Commit

Permalink
Merge pull request #1569 from evoskuil/master
Browse files Browse the repository at this point in the history
Add single byte and quarter block hash functions.
  • Loading branch information
evoskuil authored Dec 18, 2024
2 parents 2051639 + 95e9fd3 commit 998d1a8
Show file tree
Hide file tree
Showing 7 changed files with 166 additions and 2 deletions.
9 changes: 8 additions & 1 deletion include/bitcoin/system/hash/sha/algorithm.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ class algorithm

/// Byte-based types.
using byte_t = uint8_t;
using quart_t = std_array<byte_t, to_half(SHA::chunk_words) * SHA::word_bytes>;
using half_t = std_array<byte_t, SHA::chunk_words * SHA::word_bytes>;
using block_t = std_array<byte_t, SHA::block_words * SHA::word_bytes>;
using digest_t = std_array<byte_t, bytes<SHA::digest>>;
Expand All @@ -76,7 +77,7 @@ class algorithm
using iblocks_t = iterable<block_t>;
using digests_t = std::vector<digest_t>;

/// Constants.
/// Count types.
/// -----------------------------------------------------------------------

/// count_t is uint64_t (sha160/256) or uint128_t (sha512).
Expand All @@ -91,11 +92,14 @@ class algorithm

/// Single hashing.
/// -----------------------------------------------------------------------

template <size_t Size>
static constexpr digest_t hash(const ablocks_t<Size>& blocks) NOEXCEPT;
static constexpr digest_t hash(const block_t& block) NOEXCEPT;
static constexpr digest_t hash(const half_t& half) NOEXCEPT;
static constexpr digest_t hash(const half_t& left, const half_t& right) NOEXCEPT;
static constexpr digest_t hash(const quart_t& left, const quart_t& right) NOEXCEPT;
static constexpr digest_t hash(uint8_t byte) NOEXCEPT;
static digest_t hash(iblocks_t&& blocks) NOEXCEPT;

/// Double hashing (sha256/512).
Expand Down Expand Up @@ -232,6 +236,8 @@ class algorithm
/// -----------------------------------------------------------------------

INLINE static constexpr void input(buffer_t& buffer, const block_t& block) NOEXCEPT;
INLINE static constexpr void input_left(auto& buffer, const quart_t& quarter) NOEXCEPT;
INLINE static constexpr void input_right(auto& buffer, const quart_t& quarter) NOEXCEPT;
INLINE static constexpr void input_left(auto& buffer, const half_t& half) NOEXCEPT;
INLINE static constexpr void input_right(auto& buffer, const half_t& half) NOEXCEPT;
INLINE static constexpr void inject_left(auto& buffer, const auto& left) NOEXCEPT;
Expand Down Expand Up @@ -403,6 +409,7 @@ class algorithm
static digest_t native_hash(const block_t& block) NOEXCEPT;
static digest_t native_hash(const half_t& half) NOEXCEPT;
static digest_t native_hash(const half_t& left, const half_t& right) NOEXCEPT;
static digest_t native_hash(uint8_t byte) NOEXCEPT;

static digest_t native_double_hash(const block_t& block) NOEXCEPT;
static digest_t native_double_hash(const half_t& half) NOEXCEPT;
Expand Down
2 changes: 1 addition & 1 deletion include/bitcoin/system/impl/hash/accumulator.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ serialize(size_t size) NOEXCEPT
// as word_t: { [0x00000000], [0x00000200] }
//
// block_t (128 bytes), counter_t (128 bits), words_t (64 bits), byte_t (8 bits).
// One block bit count (0x0400) in SHA256 (BE):
// One block bit count (0x0400) in SHA512 (BE):
// as byte_t: { (0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00),
// (0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00) }
// as word_t: { [0x0000000000000000], [0x0000000000000400] }
Expand Down
13 changes: 13 additions & 0 deletions include/bitcoin/system/impl/hash/sha/algorithm_native.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,19 @@ native_hash(const half_t& left, const half_t& right) NOEXCEPT
return native_finalize<one>(state);
}

TEMPLATE
typename CLASS::digest_t CLASS::
native_hash(uint8_t byte) NOEXCEPT
{
constexpr auto pad = bit_hi<uint8_t>;

block_t block{};
block.at(0) = byte;
block.at(1) = pad;
block.at(63) = byte_bits;
return native_hash(block);
}

// Double hash functions start with BE data and end with BE digest_t.
// ----------------------------------------------------------------------------

Expand Down
59 changes: 59 additions & 0 deletions include/bitcoin/system/impl/hash/sha/algorithm_parsing.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,65 @@ input_right(auto& buffer, const half_t& half) NOEXCEPT
}
}

TEMPLATE
INLINE constexpr void CLASS::
input_left(auto& buffer, const quart_t& quarter) NOEXCEPT
{
using word = array_element<buffer_t>;

if (std::is_constant_evaluated())
{
constexpr auto size = SHA::word_bytes;
from_big<0 * size>(buffer.at(0), quarter);
from_big<1 * size>(buffer.at(1), quarter);
from_big<2 * size>(buffer.at(2), quarter);
from_big<3 * size>(buffer.at(3), quarter);
}
else if constexpr (bc::is_little_endian)
{
const auto& in = array_cast<word>(quarter);
buffer[0] = native_from_big_end(in[0]);
buffer[1] = native_from_big_end(in[1]);
buffer[2] = native_from_big_end(in[2]);
buffer[3] = native_from_big_end(in[3]);
}
else
{
constexpr auto short_words = to_half(SHA::chunk_words);
array_cast<word, short_words>(buffer) = array_cast<word>(quarter);
}
}

TEMPLATE
INLINE constexpr void CLASS::
input_right(auto& buffer, const quart_t& quarter) NOEXCEPT
{
using word = array_element<buffer_t>;

if (std::is_constant_evaluated())
{
constexpr auto size = SHA::word_bytes;
from_big<0 * size>(buffer.at(4), quarter);
from_big<1 * size>(buffer.at(5), quarter);
from_big<2 * size>(buffer.at(6), quarter);
from_big<3 * size>(buffer.at(7), quarter);
}
else if constexpr (bc::is_little_endian)
{
const auto& in = array_cast<word>(quarter);
buffer[4] = native_from_big_end(in[0]);
buffer[5] = native_from_big_end(in[1]);
buffer[6] = native_from_big_end(in[2]);
buffer[7] = native_from_big_end(in[3]);
}
else
{
constexpr auto short_words = to_half(SHA::chunk_words);
array_cast<word, short_words, short_words>(buffer) =
array_cast<word>(quarter);
}
}

TEMPLATE
INLINE constexpr typename CLASS::digest_t CLASS::
output(const state_t& state) NOEXCEPT
Expand Down
63 changes: 63 additions & 0 deletions include/bitcoin/system/impl/hash/sha/algorithm_single.ipp
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,69 @@ hash(const half_t& left, const half_t& right) NOEXCEPT
}
}

TEMPLATE
constexpr typename CLASS::digest_t CLASS::
hash(const quart_t& left, const quart_t& right) NOEXCEPT
{
const auto hasher = [](const quart_t& left, const quart_t& right) NOEXCEPT
{
auto state = H::get;
buffer_t buffer{};
input_left(buffer, left);
input_right(buffer, right);
pad_half(buffer);
schedule(buffer);
compress(state, buffer);
return output(state);
};

if (std::is_constant_evaluated())
{
return hasher(left, right);
}
else if constexpr (native && SHA::strength == 256)
{
return native_hash(left, right);
}
else
{
return hasher(left, right);
}
}

TEMPLATE
constexpr typename CLASS::digest_t CLASS::
hash(uint8_t byte) NOEXCEPT
{
const auto hasher = [](uint8_t byte) NOEXCEPT
{
// Shift the pad sentinel one byte to make room for the value.
// Byte swapped for the sake of simplicity and will be reversed below.
constexpr auto pad = shift_left<word_t>(bit_hi<uint8_t>, byte_bits);

auto state = H::get;
buffer_t buffer{};
buffer.at(0) = byteswap(bit_or<word_t>(pad, byte));
buffer.at(15) = byte_bits;
schedule(buffer);
compress(state, buffer);
return output(state);
};

if (std::is_constant_evaluated())
{
return hasher(byte);
}
else if constexpr (native && SHA::strength == 256)
{
return native_hash(byte);
}
else
{
return hasher(byte);
}
}

} // namespace sha
} // namespace system
} // namespace libbitcoin
Expand Down
15 changes: 15 additions & 0 deletions test/hash/sha/sha256.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,21 @@ BOOST_AUTO_TEST_CASE(sha256__hash__half_blocks__expected)
BOOST_CHECK_EQUAL(sha256::hash(sha256::half_t{ 0 }, sha256::half_t{ 0 }), expected);
}

BOOST_AUTO_TEST_CASE(sha256__hash__quart_blocks__expected)
{
constexpr auto expected = sha256::hash(sha256::quart_t{ 0 }, sha256::quart_t{ 0 });
static_assert(sha256::hash(sha256::half_t{ 0 }) == expected);
BOOST_CHECK_EQUAL(sha256::hash(sha256::quart_t{ 0 }, sha256::quart_t{ 0 }), expected);
}

BOOST_AUTO_TEST_CASE(sha256__hash__byte__expected)
{
// github.com/mit-dci/rustreexo/blob/main/src/accumulator/node_hash.rs#L338
constexpr auto expected = base16_array("6e340b9cffb37a989ca544e6bb780a2c78901d3fb33738768511a30617afa01d");
static_assert(sha256::hash(0) == expected);
BOOST_CHECK_EQUAL(sha256::hash(0), expected);
}

// sha256::double_hash
BOOST_AUTO_TEST_CASE(sha256__double_hash__full_block__expected)
{
Expand Down
7 changes: 7 additions & 0 deletions test/hash/sha/sha512.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -256,6 +256,13 @@ BOOST_AUTO_TEST_CASE(sha512__hash__half_blocks__expected)
BOOST_CHECK_EQUAL(sha512::hash(sha512::half_t{ 0 }, sha512::half_t{ 0 }), expected);
}

BOOST_AUTO_TEST_CASE(sha512__hash__quart_blocks__expected)
{
constexpr auto expected = sha512::hash(sha512::quart_t{ 0 }, sha512::quart_t{ 0 });
static_assert(sha512::hash(sha512::half_t{ 0 }) == expected);
BOOST_CHECK_EQUAL(sha512::hash(sha512::quart_t{ 0 }, sha512::quart_t{ 0 }), expected);
}

// sha512::double_hash
BOOST_AUTO_TEST_CASE(sha512__double_hash__full_block__expected)
{
Expand Down

0 comments on commit 998d1a8

Please sign in to comment.