Browse Source

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.
metaverse
Jack Grigg 8 years ago
parent
commit
881ffbfc87
  1. 79
      src/crypto/equihash.cpp
  2. 7
      src/crypto/equihash.h
  3. 27
      src/gtest/test_equihash.cpp

79
src/crypto/equihash.cpp

@ -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)

7
src/crypto/equihash.h

@ -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);

27
src/gtest/test_equihash.cpp

@ -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});

Loading…
Cancel
Save