Skip to content

Commit

Permalink
Cryptonight HeavyX and 2 minute block times HF
Browse files Browse the repository at this point in the history
Changed the algorithm to Cryptonight HeavyX (2MB scratchpad with 1 << 21 Iterations) to be ASIC, FPGA and Nicehash resistant.

Changed the block time to 2 minutes, in preparation for X-CASH Proof of Stake.

Changed the hardfork version to 12.

Changed the version to 1.5.0
  • Loading branch information
zachhildreth committed Feb 14, 2019
1 parent 85dfb6f commit 70e1ef2
Show file tree
Hide file tree
Showing 14 changed files with 268 additions and 68 deletions.
9 changes: 5 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,17 @@ Portions Copyright (c) 2012-2013 The Cryptonote developers.

## Introduction

X-CASH is a cryptocurrency built using the Cryptonight v8 (CNv2) algorithm, with the aim to become and standard in digital payment and transaction settlement. We believe privacy is very important when it comes to managing personal finances, but at the same time banks and institutions need to know the source of the funds for KYC purposes. Therefore, we plan on leaving the users the choice of whether or not they want their transaction to be public. Because we are implementing a worldwide network of dedicated servers, we hope to make the synchronization of the blockchain faster than other cryptocurrencies as well as reducing transaction latency. We believe this network will be a key component in the deployment of the future improvements we plan on adding to the core code. The main characteristics of X-CASH are detailed below:
X-CASH is a cryptocurrency built using the Cryptonight v8 (CNv2) algorithm, using a variation called CN/DOUBLE, with the aim to become and standard in digital payment and transaction settlement. We believe privacy is very important when it comes to managing personal finances, but at the same time banks and institutions need to know the source of the funds for KYC purposes. Therefore, we plan on leaving the users the choice of whether or not they want their transaction to be public. Because we are implementing a worldwide network of dedicated servers, we hope to make the synchronization of the blockchain faster than other cryptocurrencies as well as reducing transaction latency. We believe this network will be a key component in the deployment of the future improvements we plan on adding to the core code. The main characteristics of X-CASH are detailed below:

- Total Supply: 100,000,000,000

- Block Time: 1 minute
- Block Time: 2 minute

- Algorithm: Cryptonight v8 (CNv2)
- Algorithm: CN/DOUBLE (Cryptonight v8 (scratchpad of 2MB) with double the iterations, and some minor changes)

- Reward: ~100,000 XCA at inception

- Emission structure: logarithmic until max supply is reached in 2020. For more information: https://www.x-cash.org
- Emission structure: logarithmic until max supply is reached in 2022. For more information: https://www.x-cash.org

We developed the FlexPrivacy feature, giving the opportunity to the user to chose between sending their transaction privately or publicly, from the same account, on the same blockchain, and on a per transaction basis. This is the first Cryptonight coin showing this hybrid feature, resembling the current cash system:
- make a transaction using a check, credit card, bank transfer etc… and leave a transaction trail (public transaction)
Expand All @@ -54,6 +54,7 @@ If you want to help out, see [CONTRIBUTING](CONTRIBUTING.md) for a set of guidel
| 106000 | 16-10-2018 | v9 | Adjusting the new difficulty algorithm |
| 136000 | 06-11-2018 | v10 | Adding public transactions, bullet proofs, fixed ring size of 21 and more! |
| 137000 | 07-11-2018 | v11 | This version makes sure that all non bullet proof transactions are confirmed before bullet proofs transactions are required. |
| 281000 | 15-02-2019 | v12 | This version changes the proof of work algorithm to CN/DOUBLE, and changes the block time to 2 minutes. |

Note future releases block heights and dates may change, so make sure to frequently check github, our website, the forums, etc. for the most up to date information.

Expand Down
142 changes: 111 additions & 31 deletions src/crypto/slow-hash.c
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
#include "variant2_int_sqrt.h"

#define MEMORY (1 << 21) // 2MB scratchpad
#define ITER (1 << 20)
#define ITER(variant) (variant >= 3 ? (1 << 21) : (1 << 20))
#define AES_BLOCK_SIZE 16
#define AES_KEY_SIZE 32
#define INIT_SIZE_BLK 8
Expand Down Expand Up @@ -109,8 +109,16 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp
memcpy(b + AES_BLOCK_SIZE, state.hs.b + 64, AES_BLOCK_SIZE); \
xor64(b + AES_BLOCK_SIZE, state.hs.b + 80); \
xor64(b + AES_BLOCK_SIZE + 8, state.hs.b + 88); \
division_result = state.hs.w[12]; \
sqrt_result = state.hs.w[13]; \
if (variant == 2) \
{ \
division_result = state.hs.w[12]; \
sqrt_result = state.hs.w[13]; \
} \
else if (variant == 3) \
{ \
division_result = SWAP64LE(state.hs.w[12]); \
sqrt_result = SWAP64LE(state.hs.w[13]); \
} \
} while (0)

#define VARIANT2_SHUFFLE_ADD_SSE2(base_ptr, offset) \
Expand Down Expand Up @@ -145,30 +153,66 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp
const uint64_t chunk1_old[2] = { chunk1[0], chunk1[1] }; \
\
uint64_t b1[2]; \
memcpy(b1, b + 16, 16); \
chunk1[0] = chunk3[0] + b1[0]; \
chunk1[1] = chunk3[1] + b1[1]; \
\
uint64_t a0[2]; \
memcpy(a0, a, 16); \
chunk3[0] = chunk2[0] + a0[0]; \
chunk3[1] = chunk2[1] + a0[1]; \
\
uint64_t b0[2]; \
memcpy(b0, b, 16); \
chunk2[0] = chunk1_old[0] + b0[0]; \
chunk2[1] = chunk1_old[1] + b0[1]; \
if (variant == 2) \
{ \
memcpy(b1, b + 16, 16); \
chunk1[0] = chunk3[0] + b1[0]; \
chunk1[1] = chunk3[1] + b1[1]; \
\
uint64_t a0[2]; \
memcpy(a0, a, 16); \
chunk3[0] = chunk2[0] + a0[0]; \
chunk3[1] = chunk2[1] + a0[1]; \
\
uint64_t b0[2]; \
memcpy(b0, b, 16); \
chunk2[0] = chunk1_old[0] + b0[0]; \
chunk2[1] = chunk1_old[1] + b0[1]; \
} \
else if (variant == 3) \
{ \
memcpy_swap64le(b1, b + 16, 2); \
chunk1[0] = SWAP64LE(SWAP64LE(chunk3[0]) + b1[0]); \
chunk1[1] = SWAP64LE(SWAP64LE(chunk3[1]) + b1[1]); \
\
uint64_t a0[2]; \
memcpy_swap64le(a0, a, 2); \
chunk3[0] = SWAP64LE(SWAP64LE(chunk2[0]) + a0[0]); \
chunk3[1] = SWAP64LE(SWAP64LE(chunk2[1]) + a0[1]); \
\
uint64_t b0[2]; \
memcpy_swap64le(b0, b, 2); \
chunk2[0] = SWAP64LE(SWAP64LE(chunk1_old[0]) + b0[0]); \
chunk2[1] = SWAP64LE(SWAP64LE(chunk1_old[1]) + b0[1]); \
} \
} while (0)

#define VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr) \
((uint64_t*)(b))[0] ^= division_result ^ (sqrt_result << 32); \
uint64_t sqrt_input_result = 0; \
if (variant <= 2) \
{ \
const uint64_t dividend = ((uint64_t*)(ptr))[1]; \
const uint32_t divisor = (((uint64_t*)(ptr))[0] + (uint32_t)(sqrt_result << 1)) | 0x80000001UL; \
division_result = ((uint32_t)(dividend / divisor)) + \
((uint64_t*)(b))[0] ^= division_result ^ (sqrt_result << 32); \
{ \
const uint64_t dividend = ((uint64_t*)(ptr))[1]; \
const uint32_t divisor = (((uint64_t*)(ptr))[0] + (uint32_t)(sqrt_result << 1)) | 0x80000001UL; \
division_result = ((uint32_t)(dividend / divisor)) + \
(((uint64_t)(dividend % divisor)) << 32); \
} \
sqrt_input_result = ((uint64_t*)(ptr))[0] + division_result; \
} \
const uint64_t sqrt_input = ((uint64_t*)(ptr))[0] + division_result
else if (variant == 3) \
{ \
uint64_t tmpx = division_result ^ (sqrt_result << 32); \
((uint64_t*)(b))[0] ^= SWAP64LE(tmpx); \
{ \
const uint64_t dividend = SWAP64LE(((uint64_t*)(ptr))[1]); \
const uint32_t divisor = (SWAP64LE(((uint64_t*)(ptr))[0]) + (uint32_t)(sqrt_result << 1)) | 0x80000001UL; \
division_result = ((uint32_t)(dividend / divisor)) + \
(((uint64_t)(dividend % divisor)) << 32); \
} \
sqrt_input_result = SWAP64LE(((uint64_t*)(ptr))[0]) + division_result; \
} \
const uint64_t sqrt_input = sqrt_input_result

#define VARIANT2_INTEGER_MATH_SSE2(b, ptr) \
do if (variant >= 2) \
Expand Down Expand Up @@ -205,12 +249,19 @@ extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *exp
}

#define VARIANT2_2() \
do if (variant >= 2) \
do if (variant == 2) \
{ \
*U64(hp_state + (j ^ 0x10)) ^= hi; \
*(U64(hp_state + (j ^ 0x10)) + 1) ^= lo; \
hi ^= *U64(hp_state + (j ^ 0x20)); \
lo ^= *(U64(hp_state + (j ^ 0x20)) + 1); \
} while (0); \
do if (variant == 3) \
{ \
*U64(hp_state + (j ^ 0x10)) ^= SWAP64LE(hi); \
*(U64(hp_state + (j ^ 0x10)) + 1) ^= SWAP64LE(lo); \
hi ^= SWAP64LE(*U64(hp_state + (j ^ 0x20))); \
lo ^= SWAP64LE(*(U64(hp_state + (j ^ 0x20)) + 1)); \
} while (0)


Expand Down Expand Up @@ -772,7 +823,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
// the useAes test is only performed once, not every iteration.
if(useAes)
{
for(i = 0; i < ITER / 2; i++)
for(i = 0; i < ITER(variant) / 2; i++)
{
pre_aes();
_c = _mm_aesenc_si128(_c, _a);
Expand All @@ -781,7 +832,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
}
else
{
for(i = 0; i < ITER / 2; i++)
for(i = 0; i < ITER(variant) / 2; i++)
{
pre_aes();
aesb_single_round((uint8_t *) &_c, (uint8_t *) &_c, (uint8_t *) &_a);
Expand Down Expand Up @@ -1124,7 +1175,7 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
_b = vld1q_u8((const uint8_t *)b);
_b1 = vld1q_u8(((const uint8_t *)b) + AES_BLOCK_SIZE);

for(i = 0; i < ITER / 2; i++)
for(i = 0; i < ITER(variant) / 2; i++)
{
pre_aes();
_c = vaeseq_u8(_c, zero);
Expand Down Expand Up @@ -1331,7 +1382,7 @@ 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++)
for(i = 0; i < ITER(variant) / 2; i++)
{
#define MASK ((uint32_t)(((MEMORY / AES_BLOCK_SIZE) - 1) << 4))
#define state_index(x) ((*(uint32_t *) x) & MASK)
Expand Down Expand Up @@ -1412,6 +1463,7 @@ extern int aesb_single_round(const uint8_t *in, uint8_t*out, const uint8_t *expa
extern int aesb_pseudo_round(const uint8_t *in, uint8_t *out, const uint8_t *expandedKey);

static size_t e2i(const uint8_t* a, size_t count) { return (*((uint64_t*)a) / AES_BLOCK_SIZE) & (count - 1); }
static size_t e2i_3(const uint8_t* a, size_t count) { return (SWAP64LE(*((uint64_t*)a)) / AES_BLOCK_SIZE) & (count - 1); }

static void mul(const uint8_t* a, const uint8_t* b, uint8_t* res) {
uint64_t a0, b0;
Expand Down Expand Up @@ -1521,22 +1573,43 @@ 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++) {
for (i = 0; i < ITER(variant) / 2; i++) {
/* Dependency chain: address -> read value ------+
* written value <-+ hard function (AES or MUL) <+
* next address <-+
*/
/* Iteration 1 */
j = e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
if (variant <= 2)
{
j = e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
}
else if (variant == 3)
{
j = e2i_3(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);
copy_block(&long_state[j], c1);
xor_blocks(&long_state[j], b);
assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE);
if (variant <= 2)
{
assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE);
}
else if (variant == 3)
{
assert(j == e2i_3(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE);
}
VARIANT1_1(&long_state[j]);
/* Iteration 2 */
j = e2i(c1, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
if (variant <= 2)
{
j = e2i(c1, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
}
else if (variant == 3)
{
j = e2i_3(c1, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE;
}
copy_block(c2, &long_state[j]);
VARIANT2_PORTABLE_INTEGER_MATH(c2, c1);
mul(c1, c2, d);
Expand All @@ -1548,7 +1621,14 @@ void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int
xor_blocks(c1, c2);
VARIANT1_2(c2 + 8);
copy_block(&long_state[j], c2);
assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE);
if (variant <= 2)
{
assert(j == e2i(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE);
}
else if (variant == 3)
{
assert(j == e2i_3(a, MEMORY / AES_BLOCK_SIZE) * AES_BLOCK_SIZE);
}
if (variant >= 2) {
copy_block(b + AES_BLOCK_SIZE, b);
}
Expand Down
6 changes: 3 additions & 3 deletions src/cryptonote_basic/cryptonote_basic_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,9 @@ namespace cryptonote {
}
//-----------------------------------------------------------------------------------------------
bool get_block_reward(size_t median_weight, size_t current_block_weight, uint64_t already_generated_coins, uint64_t &reward, uint8_t version, uint64_t height) {
static_assert(DIFFICULTY_TARGET_V2%60==0&&DIFFICULTY_TARGET_V1%60==0,"difficulty targets must be a multiple of 60");
const int target = version < 2 ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V2;
const int target_minutes = target / 60;
static_assert(DIFFICULTY_TARGET_V1%60==0&&DIFFICULTY_TARGET_V2%60==0&&DIFFICULTY_TARGET_V8%60==0&&DIFFICULTY_TARGET_V9%60==0&&DIFFICULTY_TARGET_V10%60==0&&DIFFICULTY_TARGET_V12%60==0,"difficulty targets must be a multiple of 60");
const int target = version < HF_VERSION_TWO_MINUTE_BLOCK_TIME ? DIFFICULTY_TARGET_V1 : DIFFICULTY_TARGET_V12;
const int target_minutes = 1; // set it to always 1, so we dont increase the block reward when changing the block time
const int emission_speed_factor = EMISSION_SPEED_FACTOR_PER_MINUTE - (target_minutes-1);

uint64_t base_reward;
Expand Down
6 changes: 5 additions & 1 deletion src/cryptonote_basic/cryptonote_format_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1033,10 +1033,14 @@ namespace cryptonote
{
cn_variant = 1;
}
else
else if (b.major_version == 10 || b.major_version == 11)
{
cn_variant = 2;
}
else
{
cn_variant = 3;
}
crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant);
return true;
}
Expand Down
74 changes: 74 additions & 0 deletions src/cryptonote_basic/difficulty.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,4 +369,78 @@ difficulty_type next_difficulty_V10(std::vector<std::uint64_t> timestamps, std::
return next_difficulty;
}









// LWMA difficulty algorithm
// Background: https://github.com/zawy12/difficulty-algorithms/issues/3
// Copyright (c) 2017-2018 Zawy (pseudocode)
// MIT license http://www.opensource.org/licenses/mit-license.php
// Copyright (c) 2018 The Stellite Project
// Copyright (c) 2018 The Masari Project (10x for quicker recoveries, minimum to be symmetric with FTL)
// Copyright (c) 2018 Wownero Inc., a Monero Enterprise Alliance partner company
// Copyright (c) 2018 The Karbowanec developers (initial code)
// Copyright (c) 2018 Haven Protocol (refinements)
// Degnr8, Karbowanec, Masari, Bitcoin Gold, Bitcoin Candy, and Haven have contributed.
// This algorithm is: next_difficulty = harmonic_mean(Difficulties) * T / LWMA(Solvetimes)
// The harmonic_mean(Difficulties) = 1/average(Targets) so it is also:
// next_target = avg(Targets) * LWMA(Solvetimes) / T.
// This is "the best algorithm" because it has lowest root-mean-square error between
// needed & actual difficulty during hash attacks while having the lowest standard
// deviation during stable hashrate. That is, it's the fastest for a given stability and vice versa.
// Do not use "if solvetime < 1 then solvetime = 1" which allows a catastrophic exploit.
// Do not sort timestamps. "Solvetimes" and "LWMA" variables must allow negatives.
// Do not use MTP as most recent block. Do not use (POW)Limits, filtering, or tempering.
// Do not forget to set N (aka DIFFICULTY_WINDOW in Cryptonote) to recommendation below.
// The nodes' future time limit (FTL) aka CRYPTONOTE_BLOCK_FUTURE_TIME_LIMIT needs to
// be reduced from 60*60*2 to 500 seconds to prevent timestamp manipulation from miner's with
// > 50% hash power. If this is too small, it can be increased to 1000 at a cost in protection.
difficulty_type next_difficulty_V12(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds) {
const int64_t T = static_cast<int64_t>(target_seconds);
size_t N = DIFFICULTY_WINDOW_V12;
// Return a difficulty of 1 for first 3 blocks if it's the start of the chain.
if (timestamps.size() < 4) {
return 1;
}
// Otherwise, use a smaller N if the start of the chain is less than N+1.
else if ( timestamps.size() < N+1 ) {
N = timestamps.size() - 1;
}
// Otherwise make sure timestamps and cumulative_difficulties are correct size.
else {
timestamps.resize(N+1);
cumulative_difficulties.resize(N+1);
}
// To get an average solvetime to within +/- ~0.1%, use an adjustment factor.
// adjust=0.998 for N = 60
const double adjust = 0.998;
// The divisor k normalizes the LWMA sum to a standard LWMA.
const double k = N * (N + 1) / 2;
double LWMA(0), sum_inverse_D(0), harmonic_mean_D(0), nextDifficulty(0);
int64_t solveTime(0);
uint64_t difficulty(0), next_difficulty(0);
// Loop through N most recent blocks. N is most recently solved block.
for (size_t i = 1; i <= N; i++) {
solveTime = static_cast<int64_t>(timestamps[i]) - static_cast<int64_t>(timestamps[i - 1]);
solveTime = std::min<int64_t>((T * 10), solveTime);
difficulty = cumulative_difficulties[i] - cumulative_difficulties[i - 1];
LWMA += (int64_t)(solveTime * i) / k;
sum_inverse_D += 1 / static_cast<double>(difficulty);
}
harmonic_mean_D = N / sum_inverse_D;
// Limit LWMA same as Bitcoin's 1/4 in case something unforeseen occurs.
if (static_cast<int64_t>(boost::math::round(LWMA)) < T / 4)
LWMA = static_cast<double>(T / 4);
nextDifficulty = harmonic_mean_D * T / LWMA * adjust;
// No limits should be employed, but this is correct way to employ a 20% symmetrical limit:
// nextDifficulty=max(previous_Difficulty*0.8,min(previous_Difficulty/0.8, next_Difficulty));
next_difficulty = static_cast<uint64_t>(nextDifficulty);
return next_difficulty;
}

}
1 change: 1 addition & 0 deletions src/cryptonote_basic/difficulty.h
Original file line number Diff line number Diff line change
Expand Up @@ -56,4 +56,5 @@ namespace cryptonote
difficulty_type next_difficulty_V8(std::vector<std::uint64_t> timestamps,std::vector<difficulty_type> cumulative_difficulties, uint64_t block_height);
difficulty_type next_difficulty_V9(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds);
difficulty_type next_difficulty_V10(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds);
difficulty_type next_difficulty_V12(std::vector<std::uint64_t> timestamps, std::vector<difficulty_type> cumulative_difficulties, size_t target_seconds);
}

0 comments on commit 70e1ef2

Please sign in to comment.