Add methods for byte array expansion and compression
These methods convert between: - A byte array of length NL/8, and - An array of N blocks of ceil(L/8) bytes.
This commit is contained in:
@@ -39,6 +39,85 @@ int Equihash<N,K>::InitialiseState(eh_HashState& base_state)
|
||||
personalization);
|
||||
}
|
||||
|
||||
void ExpandArray(const unsigned char* in, size_t in_len,
|
||||
unsigned char* out, size_t out_len,
|
||||
size_t bit_len)
|
||||
{
|
||||
assert(bit_len >= 8);
|
||||
assert(8*sizeof(uint32_t) >= 7+bit_len);
|
||||
|
||||
size_t out_width { (bit_len+7)/8 };
|
||||
assert(out_len == 8*out_width*in_len/bit_len);
|
||||
|
||||
uint32_t bit_len_mask { ((uint32_t)1 << bit_len) - 1 };
|
||||
|
||||
// The acc_bits least-significant bits of acc_value represent a bit sequence
|
||||
// in big-endian order.
|
||||
size_t acc_bits = 0;
|
||||
uint32_t acc_value = 0;
|
||||
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < in_len; i++) {
|
||||
acc_value = (acc_value << 8) | in[i];
|
||||
acc_bits += 8;
|
||||
|
||||
// When we have bit_len or more bits in the accumulator, write the next
|
||||
// output element.
|
||||
if (acc_bits >= bit_len) {
|
||||
acc_bits -= bit_len;
|
||||
for (size_t x = 0; x < out_width; x++) {
|
||||
out[j+x] = (
|
||||
// Big-endian
|
||||
acc_value >> (acc_bits+(8*(out_width-x-1)))
|
||||
) & (
|
||||
// Apply bit_len_mask across byte boundaries
|
||||
(bit_len_mask >> (8*(out_width-x-1))) & 0xFF
|
||||
);
|
||||
}
|
||||
j += out_width;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CompressArray(const unsigned char* in, size_t in_len,
|
||||
unsigned char* out, size_t out_len,
|
||||
size_t bit_len)
|
||||
{
|
||||
assert(bit_len >= 8);
|
||||
assert(8*sizeof(uint32_t) >= 7+bit_len);
|
||||
|
||||
size_t in_width { (bit_len+7)/8 };
|
||||
assert(out_len == bit_len*in_len/(8*in_width));
|
||||
|
||||
uint32_t bit_len_mask { ((uint32_t)1 << bit_len) - 1 };
|
||||
|
||||
// The acc_bits least-significant bits of acc_value represent a bit sequence
|
||||
// in big-endian order.
|
||||
size_t acc_bits = 0;
|
||||
uint32_t acc_value = 0;
|
||||
|
||||
size_t j = 0;
|
||||
for (size_t i = 0; i < out_len; i++) {
|
||||
// When we have fewer than 8 bits left in the accumulator, read the next
|
||||
// input element.
|
||||
if (acc_bits < 8) {
|
||||
acc_value = acc_value << bit_len;
|
||||
for (size_t x = 0; x < in_width; x++) {
|
||||
acc_value = acc_value | (
|
||||
(
|
||||
// Apply bit_len_mask across byte boundaries
|
||||
in[j+x] & ((bit_len_mask >> (8*(in_width-x-1))) & 0xFF)
|
||||
) << (8*(in_width-x-1))); // Big-endian
|
||||
}
|
||||
j += in_width;
|
||||
acc_bits += bit_len;
|
||||
}
|
||||
|
||||
acc_bits -= 8;
|
||||
out[i] = (acc_value >> acc_bits) & 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
// Big-endian so that lexicographic array comparison is equivalent to integer
|
||||
// comparison
|
||||
void EhIndexToArray(const eh_index i, unsigned char* array)
|
||||
|
||||
@@ -24,6 +24,13 @@ typedef crypto_generichash_blake2b_state eh_HashState;
|
||||
typedef uint32_t eh_index;
|
||||
typedef uint8_t eh_trunc;
|
||||
|
||||
void ExpandArray(const unsigned char* in, size_t in_len,
|
||||
unsigned char* out, size_t out_len,
|
||||
size_t bit_len);
|
||||
void CompressArray(const unsigned char* in, size_t in_len,
|
||||
unsigned char* out, size_t out_len,
|
||||
size_t bit_len);
|
||||
|
||||
eh_index ArrayToEhIndex(const unsigned char* array);
|
||||
eh_trunc TruncateIndex(const eh_index i, const unsigned int ilen);
|
||||
|
||||
|
||||
@@ -3,6 +3,33 @@
|
||||
|
||||
#include "crypto/equihash.h"
|
||||
|
||||
void TestExpandAndCompress(const std::string &scope, size_t bit_len,
|
||||
std::vector<unsigned char> compact,
|
||||
std::vector<unsigned char> expanded)
|
||||
{
|
||||
SCOPED_TRACE(scope);
|
||||
|
||||
std::vector<unsigned char> out(expanded.size());
|
||||
ExpandArray(compact.data(), compact.size(), out.data(), out.size(), bit_len);
|
||||
EXPECT_EQ(expanded, out);
|
||||
|
||||
out.resize(compact.size());
|
||||
CompressArray(expanded.data(), expanded.size(), out.data(), out.size(), bit_len);
|
||||
EXPECT_EQ(compact, out);
|
||||
}
|
||||
|
||||
TEST(equihash_tests, expand_and_contract_arrays) {
|
||||
TestExpandAndCompress("8 11-bit chunks, all-ones", 11,
|
||||
ParseHex("ffffffffffffffffffffff"),
|
||||
ParseHex("07ff07ff07ff07ff07ff07ff07ff07ff"));
|
||||
TestExpandAndCompress("8 21-bit chunks, alternating 1s and 0s", 21,
|
||||
ParseHex("aaaaad55556aaaab55555aaaaad55556aaaab55555"),
|
||||
ParseHex("155555155555155555155555155555155555155555155555"));
|
||||
TestExpandAndCompress("16 14-bit chunks, alternating 11s and 00s", 14,
|
||||
ParseHex("cccf333cccf333cccf333cccf333cccf333cccf333cccf333cccf333"),
|
||||
ParseHex("3333333333333333333333333333333333333333333333333333333333333333"));
|
||||
}
|
||||
|
||||
TEST(equihash_tests, is_probably_duplicate) {
|
||||
std::shared_ptr<eh_trunc> p1 (new eh_trunc[4] {0, 1, 2, 3});
|
||||
std::shared_ptr<eh_trunc> p2 (new eh_trunc[4] {0, 1, 1, 3});
|
||||
|
||||
Reference in New Issue
Block a user