If we look at X16* like algoritms
we can see very simple sequence of hashes,
that directly promotes the creation of ASICs for all similar algorithms at once.
memcpy(output, hash, 32) is performed between rounds, and ASIC will make it too.
Instead of simple memcpy we can make another operation between rounds:
hash = ROTR(hash,hashOrder[i])
or
uint32_t hash[16];
hash = quicksort(hash)
and so on.
The constant position of nonce also makes ASIC easier to create.
I suggest to spread nonce values inside block data, between several dwords, and bytes.
X16r is example of bad anti-asic hash function:
extern "C" void x16r_hash(void output, const void input)
{
unsigned char _ALIGN(64) hash[128];
sph_blake512_context ctx_blake;
sph_bmw512_context ctx_bmw;
sph_groestl512_context ctx_groestl;
...
skipped
...
void *in = (void*) input;
int size = 80;
uint32_t *in32 = (uint32_t*) input;
getAlgoString(&in32[1], hashOrder);
for (int i = 0; i < 16; i++)
{
const char elem = hashOrder[i];
const uint8_t algo = elem >= 'A' ? elem - 'A' + 10 : elem - '0';
switch (algo) {
case BLAKE:
sph_blake512_init(&ctx_blake);
sph_blake512(&ctx_blake, in, size);
sph_blake512_close(&ctx_blake, hash);
break;
case BMW:
sph_bmw512_init(&ctx_bmw);
sph_bmw512(&ctx_bmw, in, size);
sph_bmw512_close(&ctx_bmw, hash);
break;
...
skipped
...
}
in = (void*) hash;
size = 64;
}
memcpy(output, hash, 32);
}