Skip to content

Commit

Permalink
Merge pull request #234 from graft-project/feature/cryptonight_waltz_…
Browse files Browse the repository at this point in the history
…support

Support of CryptoNight v8 ReverseWaltz based on CryptoNight V8
  • Loading branch information
EDDragonWolf authored Mar 4, 2019
2 parents 697a84c + dfd9b6d commit fec3ac9
Show file tree
Hide file tree
Showing 19 changed files with 397 additions and 62 deletions.
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,14 @@ Graft Network uses a fixed-schedule hard fork mechanism to implement new feature
Dates are provided in the format YYYY-MM-DD.


| Fork Date | Consensus version | Minimum Graft Network Version | Recommended Graft Network Version | Details |
| Fork Date | Consensus version | Minimum Graft Network Version | Recommended Graft Network Version | Details |
| ----------------- | ----------------- | ---------------------- | -------------------------- | ------------------ |
| 2018-01-18 | v7 | 1.0.0 | 1.0.1 | First release |
| 2018-04-10 | v8 | 1.1.0 | 1.1.2 | Anti-ASIC change from Monero (Cryptonight variant 1), Improved Difficulty Adjustment Algorithm (new algorithm based on the LWMA difficulty algorithm) |
| 2018-04-23 | v9 | 1.2.0 | 1.2.3 | Fix for Difficulty Adjustment Algorithm |
| 2018-09-17 | v10 | 1.4.4 | 1.4.5 | Block reward halved |
| 2018-10-31 | v11 | 1.5.0 | 1.5.1 | PoW algorithm from Monero v8 (CN v2), enabled checkpoints for mainnet |
| 2019-03-07 | v12 | 1.6.0 | 1.6.0 | Own PoW algorithm - CryptoNight V8 ReverseWaltz - tweaked from CryptoNight Monero v8 (CN v2) |

## Installing Graft Network from a Package

Expand All @@ -96,7 +97,7 @@ library archives (`.a`).
| CMake | 3.0.0 | NO | `cmake` | `cmake` | NO | |
| pkg-config | any | NO | `pkg-config` | `base-devel` | NO | |
| Boost | 1.58 | NO | `libboost-all-dev` | `boost` | NO | C++ libraries |
| OpenSSL | 1.0.2^^ | NO | `libssl-dev` | `openssl` | NO | sha256 sum |
| OpenSSL | 1.0.2 | NO | `libssl-dev` | `openssl` | NO | sha256 sum |
| libunbound | 1.4.16 | YES | `libunbound-dev` | `unbound` | NO | DNS resolver |
| libminiupnpc | 2.0 | YES | `libminiupnpc-dev` | `miniupnpc` | YES | NAT punching |
| libunwind | any | NO | `libunwind8-dev` | `libunwind` | YES | Stack traces |
Expand Down
4 changes: 2 additions & 2 deletions src/crypto/chacha.h
Original file line number Diff line number Diff line change
Expand Up @@ -72,14 +72,14 @@ namespace crypto {
inline void generate_chacha_key(const void *data, size_t size, chacha_key& key) {
static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key");
tools::scrubbed_arr<char, HASH_SIZE> pwd_hash;
crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/);
crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 0/*prehashed*/, 0/*modifier*/);
memcpy(&key, pwd_hash.data(), sizeof(key));
}

inline void generate_chacha_key_prehashed(const void *data, size_t size, chacha_key& key) {
static_assert(sizeof(chacha_key) <= sizeof(hash), "Size of hash must be at least that of chacha_key");
tools::scrubbed_arr<char, HASH_SIZE> pwd_hash;
crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/);
crypto::cn_slow_hash(data, size, pwd_hash.data(), 0/*variant*/, 1/*prehashed*/, 0/*modifier*/);
memcpy(&key, pwd_hash.data(), sizeof(key));
}

Expand Down
7 changes: 6 additions & 1 deletion src/crypto/hash-ops.h
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,13 @@ enum {
HASH_DATA_AREA = 136
};

#define CN_MODIFIER_NONE 0x0
#define CN_MODIFIER_WALTZ 0x1
#define CN_MODIFIER_REVERSE 0x2
#define CN_MODIFIER_REVERSE_WALTZ CN_MODIFIER_WALTZ | CN_MODIFIER_REVERSE

void cn_fast_hash(const void *data, size_t length, char *hash);
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed);
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, int modifier);

void hash_extra_blake(const void *data, size_t length, char *hash);
void hash_extra_groestl(const void *data, size_t length, char *hash);
Expand Down
8 changes: 4 additions & 4 deletions src/crypto/hash.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ namespace crypto {
return h;
}

inline void cn_slow_hash(const void *data, std::size_t length, hash &hash, int variant = 0) {
cn_slow_hash(data, length, reinterpret_cast<char *>(&hash), variant, 0/*prehashed*/);
inline void cn_slow_hash(const void *data, std::size_t length, hash &hash, int variant = 0, int modifier = 0) {
cn_slow_hash(data, length, reinterpret_cast<char *>(&hash), variant, 0/*prehashed*/, modifier);
}

inline void cn_slow_hash_prehashed(const void *data, std::size_t length, hash &hash, int variant = 0) {
cn_slow_hash(data, length, reinterpret_cast<char *>(&hash), variant, 1/*prehashed*/);
inline void cn_slow_hash_prehashed(const void *data, std::size_t length, hash &hash, int variant = 0, int modifier = 0) {
cn_slow_hash(data, length, reinterpret_cast<char *>(&hash), variant, 1/*prehashed*/, modifier);
}

inline void tree_hash(const hash *hashes, std::size_t count, hash &root_hash) {
Expand Down
133 changes: 90 additions & 43 deletions src/crypto/slow-hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -113,34 +113,34 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp
sqrt_result = state.hs.w[13]; \
} while (0)

#define VARIANT2_SHUFFLE_ADD_SSE2(base_ptr, offset) \
#define VARIANT2_SHUFFLE_ADD_SSE2(base_ptr, offset, reverse) \
do if (variant >= 2) \
{ \
const __m128i chunk1 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10))); \
const __m128i chunk1 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ (reverse ? 0x30 : 0x10)))); \
const __m128i chunk2 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20))); \
const __m128i chunk3 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ 0x30))); \
const __m128i chunk3 = _mm_load_si128((__m128i *)((base_ptr) + ((offset) ^ (reverse ? 0x10 : 0x30)))); \
_mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x10)), _mm_add_epi64(chunk3, _b1)); \
_mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x20)), _mm_add_epi64(chunk1, _b)); \
_mm_store_si128((__m128i *)((base_ptr) + ((offset) ^ 0x30)), _mm_add_epi64(chunk2, _a)); \
} while (0)

#define VARIANT2_SHUFFLE_ADD_NEON(base_ptr, offset) \
#define VARIANT2_SHUFFLE_ADD_NEON(base_ptr, offset, reverse) \
do if (variant >= 2) \
{ \
const uint64x2_t chunk1 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x10))); \
const uint64x2_t chunk1 = vld1q_u64(U64((base_ptr) + ((offset) ^ (reverse ? 0x30 : 0x10)))); \
const uint64x2_t chunk2 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x20))); \
const uint64x2_t chunk3 = vld1q_u64(U64((base_ptr) + ((offset) ^ 0x30))); \
const uint64x2_t chunk3 = vld1q_u64(U64((base_ptr) + ((offset) ^ (reverse ? 0x10 : 0x30)))); \
vst1q_u64(U64((base_ptr) + ((offset) ^ 0x10)), vaddq_u64(chunk3, vreinterpretq_u64_u8(_b1))); \
vst1q_u64(U64((base_ptr) + ((offset) ^ 0x20)), vaddq_u64(chunk1, vreinterpretq_u64_u8(_b))); \
vst1q_u64(U64((base_ptr) + ((offset) ^ 0x30)), vaddq_u64(chunk2, vreinterpretq_u64_u8(_a))); \
} while (0)

#define VARIANT2_PORTABLE_SHUFFLE_ADD(base_ptr, offset) \
#define VARIANT2_PORTABLE_SHUFFLE_ADD(base_ptr, offset, reverse) \
do if (variant >= 2) \
{ \
uint64_t* chunk1 = U64((base_ptr) + ((offset) ^ 0x10)); \
uint64_t* chunk1 = U64((base_ptr) + ((offset) ^ (reverse ? 0x30 : 0x10))); \
uint64_t* chunk2 = U64((base_ptr) + ((offset) ^ 0x20)); \
uint64_t* chunk3 = U64((base_ptr) + ((offset) ^ 0x30)); \
uint64_t* chunk3 = U64((base_ptr) + ((offset) ^ (reverse ? 0x10 : 0x30))); \
\
const uint64_t chunk1_old[2] = { chunk1[0], chunk1[1] }; \
\
Expand Down Expand Up @@ -288,8 +288,8 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp
* bit multiply.
* This code is based upon an optimized implementation by dga.
*/
#define post_aes() \
VARIANT2_SHUFFLE_ADD_SSE2(hp_state, j); \
#define post_aes(reverse) \
VARIANT2_SHUFFLE_ADD_SSE2(hp_state, j, reverse); \
_mm_store_si128(R128(c), _c); \
_mm_store_si128(R128(&hp_state[j]), _mm_xor_si128(_b, _c)); \
VARIANT1_1(&hp_state[j]); \
Expand All @@ -299,7 +299,7 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp
VARIANT2_INTEGER_MATH_SSE2(b, c); \
__mul(); \
VARIANT2_2(); \
VARIANT2_SHUFFLE_ADD_SSE2(hp_state, j); \
VARIANT2_SHUFFLE_ADD_SSE2(hp_state, j, reverse); \
a[0] += hi; a[1] += lo; \
p = U64(&hp_state[j]); \
p[0] = a[0]; p[1] = a[1]; \
Expand Down Expand Up @@ -693,7 +693,7 @@ void slow_hash_free_state(void)
* @param length the length in bytes of the data
* @param hash a pointer to a buffer in which the final 256 bit hash will be stored
*/
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed)
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, int modifier)
{
RDATA_ALIGN16 uint8_t expandedKey[240]; /* These buffers are aligned to use later with SSE functions */

Expand Down Expand Up @@ -762,30 +762,55 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
U64(b)[1] = U64(&state.k[16])[1] ^ U64(&state.k[48])[1];

/* CryptoNight Step 3: Bounce randomly 1,048,576 times (1<<20) through the mixing buffer,
* using 524,288 iterations of the following mixing function. Each execution
* performs two reads and writes from the mixing buffer.
* using 524,288 (CryptoNight) or 393,216 (CryptoNight Waltz) iterations of the following
* mixing function. Each execution performs two reads and writes from the mixing buffer.
*/
uint64_t iters = (modifier & CN_MODIFIER_WALTZ) ? (3 * ITER) / 8 : ITER / 2;

_b = _mm_load_si128(R128(b));
_b1 = _mm_load_si128(R128(b) + 1);
// Two independent versions, one with AES, one without, to ensure that
// the useAes test is only performed once, not every iteration.
if(useAes)
{
for(i = 0; i < ITER / 2; i++)
if(modifier & CN_MODIFIER_REVERSE)
{
pre_aes();
_c = _mm_aesenc_si128(_c, _a);
post_aes();
for(i = 0; i < iters; i++)
{
pre_aes();
_c = _mm_aesenc_si128(_c, _a);
post_aes(1);
}
}
else
{
for(i = 0; i < iters; i++)
{
pre_aes();
_c = _mm_aesenc_si128(_c, _a);
post_aes(0);
}
}
}
else
{
for(i = 0; i < ITER / 2; i++)
if(modifier & CN_MODIFIER_REVERSE)
{
pre_aes();
aesb_single_round((uint8_t *) &_c, (uint8_t *) &_c, (uint8_t *) &_a);
post_aes();
for(i = 0; i < iters; i++)
{
pre_aes();
aesb_single_round((uint8_t *) &_c, (uint8_t *) &_c, (uint8_t *) &_a);
post_aes(1);
}
}
else
{
for(i = 0; i < iters; i++)
{
pre_aes();
aesb_single_round((uint8_t *) &_c, (uint8_t *) &_c, (uint8_t *) &_a);
post_aes(0);
}
}
}

Expand Down Expand Up @@ -891,8 +916,8 @@ union cn_slow_hash_state
_c = vld1q_u8(&hp_state[j]); \
_a = vld1q_u8((const uint8_t *)a); \

#define post_aes() \
VARIANT2_SHUFFLE_ADD_NEON(hp_state, j); \
#define post_aes(reverse) \
VARIANT2_SHUFFLE_ADD_NEON(hp_state, j, reverse); \
vst1q_u8((uint8_t *)c, _c); \
vst1q_u8(&hp_state[j], veorq_u8(_b, _c)); \
VARIANT1_1(&hp_state[j]); \
Expand All @@ -902,7 +927,7 @@ union cn_slow_hash_state
VARIANT2_PORTABLE_INTEGER_MATH(b, c); \
__mul(); \
VARIANT2_2(); \
VARIANT2_SHUFFLE_ADD_NEON(hp_state, j); \
VARIANT2_SHUFFLE_ADD_NEON(hp_state, j, reverse); \
a[0] += hi; a[1] += lo; \
p = U64(&hp_state[j]); \
p[0] = a[0]; p[1] = a[1]; \
Expand Down Expand Up @@ -1040,7 +1065,7 @@ STATIC INLINE void aes_pseudo_round_xor(const uint8_t *in, uint8_t *out, const u
}
}

void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed)
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, int modifier)
{
RDATA_ALIGN16 uint8_t expandedKey[240];
RDATA_ALIGN16 uint8_t hp_state[MEMORY];
Expand Down Expand Up @@ -1090,20 +1115,36 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
U64(b)[1] = U64(&state.k[16])[1] ^ U64(&state.k[48])[1];

/* CryptoNight Step 3: Bounce randomly 1,048,576 times (1<<20) through the mixing buffer,
* using 524,288 iterations of the following mixing function. Each execution
* performs two reads and writes from the mixing buffer.
* using 524,288 (CryptoNight) or 393,216 (CryptoNight Waltz) iterations of the following
* mixing function. Each execution performs two reads and writes from the mixing buffer.
*/

_b = vld1q_u8((const uint8_t *)b);
_b1 = vld1q_u8(((const uint8_t *)b) + AES_BLOCK_SIZE);

for(i = 0; i < ITER / 2; i++)
uint64_t iters = (modifier & CN_MODIFIER_WALTZ) ? (3 * ITER) / 8 : ITER / 2;

if(modifier & CN_MODIFIER_REVERSE)
{
pre_aes();
_c = vaeseq_u8(_c, zero);
_c = vaesmcq_u8(_c);
_c = veorq_u8(_c, _a);
post_aes();
for(i = 0; i < iters; i++)
{
pre_aes();
_c = vaeseq_u8(_c, zero);
_c = vaesmcq_u8(_c);
_c = veorq_u8(_c, _a);
post_aes(1);
}
}
else
{
for(i = 0; i < iters; i++)
{
pre_aes();
_c = vaeseq_u8(_c, zero);
_c = vaesmcq_u8(_c);
_c = veorq_u8(_c, _a);
post_aes(0);
}
}

/* CryptoNight Step 4: Sequentially pass through the mixing buffer and use 10 rounds
Expand Down Expand Up @@ -1246,7 +1287,7 @@ STATIC INLINE void xor_blocks(uint8_t* a, const uint8_t* b)
U64(a)[1] ^= U64(b)[1];
}

void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed)
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, int modifier)
{
uint8_t text[INIT_SIZE_BYTE];
uint8_t a[AES_BLOCK_SIZE];
Expand Down Expand Up @@ -1301,7 +1342,10 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
U64(b)[0] = U64(&state.k[16])[0] ^ U64(&state.k[48])[0];
U64(b)[1] = U64(&state.k[16])[1] ^ U64(&state.k[48])[1];

for(i = 0; i < ITER / 2; i++)
uint64_t iters = (modifier & CN_MODIFIER_WALTZ) ? (3 * ITER) / 8 : ITER / 2;
uint8_t isReverse = (modifier & CN_MODIFIER_REVERSE) ? 1 : 0;

for(i = 0; i < iters; i++)
{
#define MASK ((uint32_t)(((MEMORY / AES_BLOCK_SIZE) - 1) << 4))
#define state_index(x) ((*(uint32_t *) x) & MASK)
Expand All @@ -1312,7 +1356,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
aesb_single_round(p, p, a);
copy_block(c1, p);

VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j);
VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j, isReverse);
xor_blocks(p, b);
VARIANT1_1(p);

Expand All @@ -1324,7 +1368,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
VARIANT2_PORTABLE_INTEGER_MATH(c, c1);
mul(c1, c, d);
VARIANT2_2_PORTABLE();
VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j);
VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j, isReverse);
sum_half_blocks(a, d);
swap_blocks(a, c);
xor_blocks(a, c);
Expand Down Expand Up @@ -1448,7 +1492,7 @@ union cn_slow_hash_state {
};
#pragma pack(pop)

void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed) {
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, int modifier) {
uint8_t long_state[MEMORY];
union cn_slow_hash_state state;
uint8_t text[INIT_SIZE_BYTE];
Expand Down Expand Up @@ -1486,7 +1530,10 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
b[i] = state.k[AES_BLOCK_SIZE + i] ^ state.k[AES_BLOCK_SIZE * 3 + i];
}

for (i = 0; i < ITER / 2; i++) {
uint64_t iters = (modifier & CN_MODIFIER_WALTZ) ? (3 * ITER) / 8 : ITER / 2;
uint8_t isReverse = (modifier & CN_MODIFIER_REVERSE) ? 1 : 0;

for (i = 0; i < iters; i++) {
/* Dependency chain: address -> read value ------+
* written value <-+ hard function (AES or MUL) <+
* next address <-+
Expand All @@ -1495,7 +1542,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
j = e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
copy_block(c1, &long_state[j]);
aesb_single_round(c1, c1, a);
VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j);
VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j, isReverse);
copy_block(&long_state[j], c1);
xor_blocks(&long_state[j], b);
assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE);
Expand All @@ -1506,7 +1553,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
VARIANT2_PORTABLE_INTEGER_MATH(c2, c1);
mul(c1, c2, d);
VARIANT2_2_PORTABLE();
VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j);
VARIANT2_PORTABLE_SHUFFLE_ADD(long_state, j, isReverse);
swap_blocks(a, c1);
sum_half_blocks(c1, d);
swap_blocks(c1, c2);
Expand Down
3 changes: 2 additions & 1 deletion src/cryptonote_basic/cryptonote_format_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,8 @@ namespace cryptonote
// variant = 1 for versions between 8 and 11
// variant = 2 for versions 11 and greater
const int cn_variant = b.major_version < 8 ? 0 : b.major_version >= 11 ? 2 : 1;
crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant);
const int cn_modifier = b.major_version < 12 ? CN_MODIFIER_NONE : CN_MODIFIER_REVERSE_WALTZ;
crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant, cn_modifier);
return true;
}
//---------------------------------------------------------------
Expand Down
8 changes: 6 additions & 2 deletions src/cryptonote_core/blockchain.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,9 @@ static const struct {
// hf 10 - decrease block reward, 2018-09-17
{ 10, 176000, 0, 1537142400 },
// hf 11 - Monero V8/CN variant 2 PoW, ~2018-10-31T17:00:00+00
{ 11, 207700, 0, 1541005200 }
{ 11, 207700, 0, 1541005200 },
// hf 12 - Graft CryptoNight Reverse Waltz PoW, ~2019-03-07T05:00:00+00
{ 12, 299200, 0, 1551934800 }
};
// static const uint64_t mainnet_hard_fork_version_1_till = 1009826;
static const uint64_t mainnet_hard_fork_version_1_till = 1;
Expand Down Expand Up @@ -147,7 +149,9 @@ static const struct {
// hf 10 - decrease block reward, 2018-09-12
{ 10, 164550, 0, 1536760800 },
// hf 11 - Monero V8/CN variant 2 PoW, 2018-10-24
{ 11, 194130, 0, 1540400400 }
{ 11, 194130, 0, 1540400400 },
// hf 12 - Graft CryptoNight Reverse Waltz PoW, ~2019-03-05T05:00:00+00
{ 12, 286500, 0, 1551762000 }
};
// static const uint64_t testnet_hard_fork_version_1_till = 624633;
static const uint64_t testnet_hard_fork_version_1_till = 1;
Expand Down
Loading

0 comments on commit fec3ac9

Please sign in to comment.