CNAv1 is known to be vulnerable to pool-creation with some simple mods. That being the case, does Blur intend to fight against pools or to embrace them and keep the GPU resistance?
Pool resistance was not the objective with the algorithm changes... more so, a welcomed byproduct of them. Pooled hashing is something that will become a part of the project further down the line, with a limit on poolsize based on % of net hash. This achieves the goal of decentralization without exposing miners to the unbalanced "luck" of solving for the correct block hash ... Although, luck and randomness are *technically* more fair.
As someone who is not intimately familiar with how the cn-Adaptive algo works, would you be able to give a simplified explanation of how the algo manages to be GPU and pool resistant?
Definitely. It's really not all that complex, to be honest. Explaining it can be, though. NERVA's implementation currently adds a few more layers for randomness. We'll be adding some of that on our own in the next network update which should be this week sometime. Essentially, the algorithm runs a base number of iterations in the cryptonote algorithm... then dynamically adds an additional number of iterations (which differs each block) to that base number. That dynamic portion is based on the previous block's height. The way it is written, the iterations in a block cycles up and then back down to make a pattern of difficulty that looks like a sawtooth.
if you notice hashrate gradually increasing, before dropping sharply, and then repeating the cycle... this is why.
There are two files that hold the algorithm's nuts and bolts, as they are present in BLUR's algorithm currently. These two files that we are going to look at, are located at the following locations in the source code:
blur/src/crypto/slow-hash.c
and
blur/src/cryptonote_basic/cryptonote_format_utils.cpp
The slow-hash.c file has a few pieces of information that are important and differentiate CN-Adaptive from other variants of cryptonight. The first of which is on line 46:
#define MEMORY (1 << 20) // 1MB scratchpad
As annotated, this parameter defines the scratchpad size for the algorithm's rounds of iterations. A 1MB scratchpad is also seen in Cryptonight-Light. This size allows for less iterations in a round, and fits within the cache of lower end CPUs, enabling older hardware to still mine somewhat effectively.
Now within the cryptonight algorithm, there is an added parameter in CN-Adaptive, for iterations. This parameter defined as
iters, and you can find the implementation on Line 570 of the same
slow-hash.c file:
void cn_slow_hash(const void *data, size_t length, char *hash, int variant, int prehashed, size_t iters)
As well as lines 645-662:
if(useAes)
{
for(i = 0; i < iters; i++)
{
pre_aes();
_c = _mm_aesenc_si128(_c, _a);
post_aes();
}
}
else
{
for(i = 0; i < iters; i++)
{
pre_aes();
aesb_single_round((uint8_t *) &_c, (uint8_t *) &_c, (uint8_t *) &_a);
post_aes();
}
}
This variable "
iters" calls out to a function in the second file I mentioned, to determine its value in the block being mined. So head over to
blur/src/cryptonote_basic/cryptonote_format_utils.cpp if you're following along.
Within that file, on lines 891-901, you'll find the calculations for the number of iterations. Before elaborating on the code, I would like to note that in its current state this code
could be optimized with a GPU. However, you would still need a CPU present in some form, to direct the GPUs hashpower. BLUR's goal is being too resource intensive for GPU/ASIC miners to pursue. In its current state, and at the current value of BLUR... this has sufficed. However, we are hardforking to a new implementation shortly, for the purpose of maintaining that as the project grows.
With that said, on lines 891-901 of
cryptonote_format_utils.cpp we have:
//---------------------------------------------------------------
bool get_block_longhash(const block& b, crypto::hash& res, uint64_t height)
{
blobdata bd = get_block_hashing_blob(b);
int cn_variant = b.major_version >= 5 ? 1 : 0;
int cn_iters = b.major_version >= 6 ? ( b.major_version >= 7 ? 0x40000 : 0x20000 ) : 0x80000;
cn_iters += ((height + 1) % 1024);
crypto::cn_slow_hash(bd.data(), bd.size(), res, cn_variant, cn_iters);
return true;
}
//---------------------------------------------------------------
The lines here that we are primarily looking at, are the ones that declare and calculate the variable "cn_iters". Starting with the first mention (where we calculate the base number of iterations):
int cn_iters = b.major_version >= 6 ? ( b.major_version >= 7 ? 0x40000 : 0x20000 ) : 0x80000;
The code above breaks down into two parts...
b.major_version >= 6 ? (x) : (0x80000)
and
b.major_version >= 7 ? (0x40000) : (0x20000)
The "x" variable in the first part, is simply swapped out for the entire "second part" in the actual code. For the purposes of explaining, its easier to break it apart this way. The "?" symbol in C++ is what's known as a ternary operator. It essentially serves as an "if" statement directed by the ">=" operators that come before it, in this instance.
So it means to say "If the block version number (hardfork version) is greater-than-or-equal-to 6, define the iterations as the value on the left-hand side of the colon. If it is not, define as 0x80000 or the value following the colon.
The number 0x80000 is in hexidecimal, and translates to the human-readable number (in decimal format) 524288, for our base # of iterations. Assuming we are above block version 5... we then move to the next conditional statement
b.major_version >= 7 ? (0x40000) : (0x20000)
.
We use the same logic here, to say if we are on block version 7 or higher, we use 0x40000 or 262144 for the base amount of iterations. If we are not (i.e. less than 7 but greater than 5), we use 0x20000 or 131072 for the base number. Now, ONTO THE DYNAMIC PART. That's much simpler:
So far we have
iterations (for current hardfork) = 262144 + (dynamic portion). The dynamic part is found on line #897, directly after the calculation for the base number:
cn_iters =+ ((height +1) % 1024)
This means to say: "Add to the base number of iterations, the calculated value for the function
(current block height) Modulo (1024). A modulo just means divide the height by 1024, and look at your remainder. That remainder is actually then the solution to the equation. So, (1025) % 1024 would equal 1. Just the same, (2047) % 1024 would equal 1023, because 2047 = 1024 + 1023...