Browse Source

added cnv4

pull/123/head
notgiven688 2 years ago
parent
commit
aac0cb5399
12 changed files with 783 additions and 108 deletions
  1. +2
    -2
      SDK/miner_raw/mine.html
  2. +1
    -1
      SDK/miner_raw/miner/cn.js
  3. +3
    -3
      SDK/miner_raw/miner/miner.js
  4. +5
    -3
      SDK/miner_raw/miner/worker.js
  5. +163
    -11
      hash_cn/libhash/cryptonight.c
  6. +1
    -5
      hash_cn/webassembly/Makefile
  7. +134
    -14
      hash_cn/webassembly/cryptonight.c
  8. +1
    -1
      hash_cn/webassembly/cryptonight.h
  9. +3
    -7
      hash_cn/webassembly/main.c
  10. +26
    -60
      hash_cn/webassembly/simple_profile.html
  11. +444
    -0
      hash_cn/webassembly/variant4_random_math.h
  12. +0
    -1
      server/Server/PoolConnection.cs

+ 2
- 2
SDK/miner_raw/mine.html View File

@ -24,8 +24,8 @@
/* start mining, use a local server */
server = "ws://localhost:8181";
startMining("killallasics",
"9v4vTVwqZzfjCFyPi7b9Uv1hHntJxycC4XvRyEscqwtq8aycw5xGpTxFyasurgf2KRBfbdAJY4AVcemL1JCegXU4EZfMtaz");
startMining("moneroocean.stream",
"422QQNhnhX8hmMEkF3TWePWSvKm6DiV7sS3Za2dXrynsJ1w8U6AzwjEdnewdhmP3CDaqvaS6BjEjGMK9mnumtufvLmz5HJi");
/* keep us updated */

+ 1
- 1
SDK/miner_raw/miner/cn.js
File diff suppressed because it is too large
View File


+ 3
- 3
SDK/miner_raw/miner/miner.js View File

@ -62,7 +62,7 @@ var openWebSocket = function () {
}
var splitted = server.split(";")
var chosen = splitted[Math.floor(Math.random() * Math.floor(splitted.length))];
var chosen = splitted[Math.floor(Math.random() * splitted.length)];
ws = new WebSocket(chosen);
@ -165,7 +165,7 @@ function startMiningWithId(loginid, numThreads = -1, userid = "") {
identifier: "handshake",
loginid: loginid,
userid: userid,
version: 6
version: 7
};
startBroadcast(() => { addWorkers(numThreads); reconnector(); });
@ -185,7 +185,7 @@ function startMining(pool, login, password = "", numThreads = -1, userid = "") {
login: login,
password: password,
userid: userid,
version: 6
version: 7
};
startBroadcast(() => { addWorkers(numThreads); reconnector(); });

+ 5
- 3
SDK/miner_raw/miner/worker.js View File

@ -4,7 +4,7 @@
importScripts('cn.js'); // imports the cn.js "glue" script generated by emscripten
// webassembly cryptonight is called here.
var cn = Module.cwrap('hash_cn', 'string', ['string', 'string', 'number', 'number']);
var cn = Module.cwrap('hash_cn', 'string', ['string', 'number', 'number', 'number']);
// A few helper (string) functions to help us working with the hex string
// which is used
@ -44,12 +44,14 @@ onmessage = function (e) {
var target = hex2int(job.target);
var inonce = getRandomInt(0, 0xFFFFFFFF);
hexnonce = int2hex(inonce);
var blob = job.blob.substring(0,78) + hexnonce + job.blob.substring(86,job.blob.length);
try {
if(job.algo === "cn")
hash = cn(job.blob, hexnonce, 0, job.variant);
hash = cn(blob, 0, job.variant, job.height);
else if(job.algo === "cn-lite")
hash = cn(job.blob, hexnonce, 1, job.variant);
hash = cn(blob, 1, job.variant, job.height);
else throw "algorithm not supported!";
var hashval = hex2int(hash.substring(56, 64));

+ 163
- 11
hash_cn/libhash/cryptonight.c View File

@ -8,6 +8,7 @@
#include "groestl.h"
#include "oaes_lib.h"
#include "variant2_int_sqrt.h"
#include "variant4_random_math.h"
#define MEMORY (1 << 21) /* 2 MiB */
#define ITER (1 << 20)
@ -18,6 +19,9 @@
#define U64(x) ((uint64_t *) (x))
// -------------------------------------- VARIANT 1 -----------------------------------------------
#define VARIANT1_1(p) \
do \
{ \
@ -33,14 +37,15 @@
xor64(p, tweak1_2); \
} while(0)
#define VARIANT1_INIT64() \
const uint64_t tweak1_2 = (variant == 1) ? *(const uint64_t *)(((const uint8_t *)input) + 35) ^ ctx->state.hs.w[24] : 0
// -------------------------------------- VARIANT 2/3 ---------------------------------------------
#define VARIANT2_INIT64() \
uint64_t division_result = 0; \
uint64_t sqrt_result = 0; \
do if (variant >= 2) \
do if ((variant == 2) || (variant == 3)) \
{ \
U64(ctx->d)[0] = ctx->state.hs.w[8] ^ ctx->state.hs.w[10]; \
U64(ctx->d)[1] = ctx->state.hs.w[9] ^ ctx->state.hs.w[11]; \
@ -48,7 +53,6 @@
sqrt_result = ctx->state.hs.w[13]; \
} while (0)
#define VARIANT2_2_PORTABLE() \
do \
{ \
@ -96,7 +100,7 @@
chunk2[0] = chunk1_old0 + ((uint64_t*) ctx->c)[0]; \
chunk2[1] = chunk1_old1 + ((uint64_t*) ctx->c)[1]; \
} while (0)
#define VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr) \
((uint64_t*)(b))[0] ^= division_result ^ (sqrt_result << 32); \
@ -120,13 +124,129 @@
#else
// double precision floating point type is not good enough on current platform
// fall back to the reference code (integer only)
#define VARIANT2_PORTABLE_INTEGER_MATH(b, ptr) \
#define VARIANT2_PORTABLE_INTEGER_MATH(b, ptr) \
do \
{ \
VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \
VARIANT2_INTEGER_MATH_SQRT_STEP_REF(); \
} while (0)
#endif
// -------------------------------------- VARIANT 4 ---------------------------------------------
struct V4_Instruction code[NUM_INSTRUCTIONS_MAX + 1];
v4_reg r[9];
#define V4_REG_LOAD(dst, src) \
do { \
memcpy((dst), (src), sizeof(v4_reg)); \
} while (0)
#define VARIANT4_RANDOM_MATH_INIT() \
do if (variant >= 4) \
{ \
for (int i = 0; i < 4; ++i) \
V4_REG_LOAD(r + i, (uint8_t*)(ctx->state.hs.w + 12) + sizeof(v4_reg) * i); \
v4_random_math_init(code, height); \
} while (0)
#define VARIANT4_RANDOM_MATH2(a, b, r, _b, _b1) \
do if (variant >= 4) \
{ \
uint64_t t[2]; \
memcpy(t, b, sizeof(uint64_t)); \
\
if (sizeof(v4_reg) == sizeof(uint32_t)) \
t[0] ^= ((r[0] + r[1]) | ((uint64_t)(r[2] + r[3]) << 32)); \
else \
t[0] ^= ((r[0] + r[1]) ^ (r[2] + r[3])); \
\
memcpy(b, t, sizeof(uint64_t)); \
\
V4_REG_LOAD(r + 4, a); \
V4_REG_LOAD(r + 5, (uint64_t*)(a) + 1); \
V4_REG_LOAD(r + 6, _b); \
V4_REG_LOAD(r + 7, _b1); \
V4_REG_LOAD(r + 8, (uint64_t*)(_b1) + 1); \
\
v4_random_math2(code1, code2, code3, code4, r); \
\
memcpy(t, a, sizeof(uint64_t) * 2); \
\
if (sizeof(v4_reg) == sizeof(uint32_t)) { \
t[0] ^= (r[2] | ((uint64_t)(r[3]) << 32)); \
t[1] ^= (r[0] | ((uint64_t)(r[1]) << 32)); \
} else { \
t[0] ^= (r[2] ^ r[3]); \
t[1] ^= (r[0] ^ r[1]); \
} \
memcpy(a, t, sizeof(uint64_t) * 2); \
} while (0)
#define VARIANT4_RANDOM_MATH(a, b, r, _b, _b1) \
do if (variant >= 4) \
{ \
uint64_t t[2]; \
memcpy(t, b, sizeof(uint64_t)); \
\
if (sizeof(v4_reg) == sizeof(uint32_t)) \
t[0] ^= ((r[0] + r[1]) | ((uint64_t)(r[2] + r[3]) << 32)); \
else \
t[0] ^= ((r[0] + r[1]) ^ (r[2] + r[3])); \
\
memcpy(b, t, sizeof(uint64_t)); \
\
V4_REG_LOAD(r + 4, a); \
V4_REG_LOAD(r + 5, (uint64_t*)(a) + 1); \
V4_REG_LOAD(r + 6, _b); \
V4_REG_LOAD(r + 7, _b1); \
V4_REG_LOAD(r + 8, (uint64_t*)(_b1) + 1); \
\
v4_random_math(code, r); \
\
memcpy(t, a, sizeof(uint64_t) * 2); \
\
if (sizeof(v4_reg) == sizeof(uint32_t)) { \
t[0] ^= (r[2] | ((uint64_t)(r[3]) << 32)); \
t[1] ^= (r[0] | ((uint64_t)(r[1]) << 32)); \
} else { \
t[0] ^= (r[2] ^ r[3]); \
t[1] ^= (r[0] ^ r[1]); \
} \
memcpy(a, t, sizeof(uint64_t) * 2); \
} while (0)
#define VARIANT4_PORTABLE_SHUFFLE_ADD(out,a_,b_, base_ptr, offset) \
do \
{ \
uint64_t* chunk1 = U64((base_ptr) + ((offset) ^ 0x10)); \
uint64_t* chunk2 = U64((base_ptr) + ((offset) ^ 0x20)); \
uint64_t* chunk3 = U64((base_ptr) + ((offset) ^ 0x30)); \
uint64_t* out_chunk = U64(out);\
\
uint64_t chunk1_old0 = chunk1[0]; \
uint64_t chunk1_old1 = chunk1[1]; \
uint64_t chunk2_old0 = chunk2[0]; \
uint64_t chunk2_old1 = chunk2[1]; \
uint64_t chunk3_old0 = chunk3[0]; \
uint64_t chunk3_old1 = chunk3[1]; \
\
chunk1[0] = chunk3_old0 + ((uint64_t*) ctx->d)[0]; \
chunk1[1] = chunk3_old1 + ((uint64_t*) ctx->d)[1]; \
\
chunk3[0] = chunk2_old0 + ((uint64_t*) a_)[0]; \
chunk3[1] = chunk2_old1 + ((uint64_t*) a_)[1]; \
\
chunk2[0] = chunk1_old0 + ((uint64_t*) b_)[0]; \
chunk2[1] = chunk1_old1 + ((uint64_t*) b_b)[1]; \
\
chunk1_old0 ^= chunk2_old0; \
chunk1_old1 ^= chunk2_old1; \
out_chunk[0] ^= chunk3_old0; \
out_chunk[1] ^= chunk3_old1; \
out_chunk[0] ^= chunk1_old0; \
out_chunk[1] ^= chunk1_old1; \
} while (0)
static void xor64(uint8_t *a, const uint64_t b)
{
@ -197,6 +317,7 @@ struct cryptonight_ctx
uint8_t c[AES_BLOCK_SIZE] __attribute__((aligned(16)));
uint8_t d[AES_BLOCK_SIZE] __attribute__((aligned(16)));
uint8_t e[AES_BLOCK_SIZE] __attribute__((aligned(16)));
uint8_t a1[AES_BLOCK_SIZE] __attribute__((aligned(16)));
oaes_ctx *aes_ctx;
};
@ -506,7 +627,7 @@ void cryptonight_hash_ctx(void *output, const void *input, size_t len, struct cr
VARIANT1_2(&ctx->long_state[j] + 8);
}
}
else
else if(variant == 2 || variant == 3)
{
for (i = 0; likely(i < ITER / 4); ++i)
{
@ -518,7 +639,7 @@ void cryptonight_hash_ctx(void *output, const void *input, size_t len, struct cr
j = ((uint32_t *)ctx->c)[0] & 0x1FFFF0;
VARIANT2_PORTABLE_INTEGER_MATH(&ctx->long_state[j], ctx->c);
mul64to128(ctx->c, &ctx->long_state[j], ctx->e);
VARIANT2_2_PORTABLE();
VARIANT2_2_PORTABLE();
VARIANT2_PORTABLE_SHUFFLE_ADD1(ctx->long_state, j);
sum_xor_dst(ctx->e, ctx->a, &ctx->long_state[j]);
copy_block(ctx->d, ctx->b);
@ -531,12 +652,43 @@ void cryptonight_hash_ctx(void *output, const void *input, size_t len, struct cr
j = ((uint32_t *)ctx->b)[0] & 0x1FFFF0;
VARIANT2_PORTABLE_INTEGER_MATH(&ctx->long_state[j], ctx->b);
mul64to128(ctx->b, &ctx->long_state[j], ctx->e);
VARIANT2_2_PORTABLE();
VARIANT2_2_PORTABLE();
VARIANT2_PORTABLE_SHUFFLE_ADD2(ctx->long_state, j);
sum_xor_dst(ctx->e, ctx->a, &ctx->long_state[j]);
copy_block(ctx->d, ctx->c);
}
}
else
{
for (i = 0; likely(i < ITER / 4); ++i)
{
j = ((uint32_t *)(ctx->a))[0] & 0x1FFFF0;
SubAndShiftAndMixAddRound((uint32_t *)ctx->c, &ctx->long_state[j], (uint32_t *)ctx->a);
VARIANT4_PORTABLE_SHUFFLE_ADD(ctx->c, ctx->a,ctx->b, ctx->long_state, j);
xor_blocks_dst(ctx->c, ctx->b, &ctx->long_state[j]);
j = ((uint32_t *)ctx->c)[0] & 0x1FFFF0;
copy_block(ctx->a1,ctx->a);
VARIANT4_RANDOM_MATH(ctx->a, &ctx->long_state[j], r, ctx->b, ctx->d);
mul64to128(ctx->c, &ctx->long_state[j], ctx->e);
VARIANT4_PORTABLE_SHUFFLE_ADD(ctx->c, ctx->a1,ctx->b, ctx->long_state, j);
sum_xor_dst(ctx->e, ctx->a, &ctx->long_state[j]);
copy_block(ctx->d, ctx->b);
j = ((uint32_t *)(ctx->a))[0] & 0x1FFFF0;
SubAndShiftAndMixAddRound((uint32_t *)ctx->b, &ctx->long_state[j], (uint32_t *)ctx->a);
VARIANT4_PORTABLE_SHUFFLE_ADD(ctx->b, ctx->a,ctx->c, ctx->long_state, j);
xor_blocks_dst(ctx->b, ctx->c, &ctx->long_state[j]);
j = ((uint32_t *)ctx->b)[0] & 0x1FFFF0;
copy_block(ctx->a1,ctx->a);
VARIANT4_RANDOM_MATH(ctx->a, &ctx->long_state[j], r, ctx->c, ctx->d);
mul64to128(ctx->b, &ctx->long_state[j], ctx->e);
VARIANT4_PORTABLE_SHUFFLE_ADD(ctx->b, ctx->a1,ctx->c, ctx->long_state, j);
sum_xor_dst(ctx->e, ctx->a, &ctx->long_state[j]);
copy_block(ctx->d, ctx->c);
}
}
memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE);
@ -672,7 +824,7 @@ void cryptonight_hash_ctx_lite(void *output, const void *input, size_t len, stru
j = ((uint32_t *)ctx->c)[0] & 0x0FFFF0;
VARIANT2_PORTABLE_INTEGER_MATH(&ctx->long_state[j], ctx->c);
mul64to128(ctx->c, &ctx->long_state[j], ctx->e);
VARIANT2_2_PORTABLE();
VARIANT2_2_PORTABLE();
VARIANT2_PORTABLE_SHUFFLE_ADD1(ctx->long_state, j);
sum_xor_dst(ctx->e, ctx->a, &ctx->long_state[j]);
copy_block(ctx->d, ctx->b);
@ -685,7 +837,7 @@ void cryptonight_hash_ctx_lite(void *output, const void *input, size_t len, stru
j = ((uint32_t *)ctx->b)[0] & 0x0FFFF0;
VARIANT2_PORTABLE_INTEGER_MATH(&ctx->long_state[j], ctx->b);
mul64to128(ctx->b, &ctx->long_state[j], ctx->e);
VARIANT2_2_PORTABLE();
VARIANT2_2_PORTABLE();
VARIANT2_PORTABLE_SHUFFLE_ADD2(ctx->long_state, j);
sum_xor_dst(ctx->e, ctx->a, &ctx->long_state[j]);
copy_block(ctx->d, ctx->c);
@ -746,4 +898,4 @@ void cryptonight(void *output, const void *input, size_t len, int lite, int vari
free(ctx);
}
}
}

+ 1
- 5
hash_cn/webassembly/Makefile View File

@ -1,11 +1,7 @@
TARGET = prog
LIBS = -lm
# Looks like there is a regression in the emcc compiler. In my tests
# the compiled code made with current versions are slower than the
# 1.37.40 reference.
CC = ~/.emcc_wrapper/1.37.40/emscripten/emcc -O3 -s SINGLE_FILE=1 -s NO_FILESYSTEM=1 -s 'EXTRA_EXPORTED_RUNTIME_METHODS=["ccall", "cwrap"]' --llvm-lto 1 -s TOTAL_MEMORY=67108864 -s WASM=1 -s "BINARYEN_TRAP_MODE='allow'" -s EXPORTED_FUNCTIONS="['_hash_cn']" --shell-file html_template/shell_minimal.html
CC = emcc -O3 -s SINGLE_FILE=1 -s NO_FILESYSTEM=1 -s 'EXTRA_EXPORTED_RUNTIME_METHODS=["ccall", "cwrap"]' --llvm-lto 1 -s TOTAL_MEMORY=67108864 -s WASM=1 -s "BINARYEN_TRAP_MODE='allow'" -s EXPORTED_FUNCTIONS="['_hash_cn']" --shell-file html_template/shell_minimal.html
CFLAGS = -Wall

+ 134
- 14
hash_cn/webassembly/cryptonight.c View File

@ -8,6 +8,7 @@
#include "groestl.h"
#include "oaes_lib.h"
#include "variant2_int_sqrt.h"
#include "variant4_random_math.h"
#define MEMORY (1 << 21) /* 2 MiB */
#define ITER (1 << 20)
@ -18,6 +19,9 @@
#define U64(x) ((uint64_t *) (x))
// -------------------------------------- VARIANT 1 -----------------------------------------------
#define VARIANT1_1(p) \
do \
{ \
@ -33,9 +37,10 @@
xor64(p, tweak1_2); \
} while(0)
#define VARIANT1_INIT64() \
const uint64_t tweak1_2 = (variant == 1) ? *(const uint64_t *)(((const uint8_t *)input) + 35) ^ ctx->state.hs.w[24] : 0
// -------------------------------------- VARIANT 2/3 ---------------------------------------------
#define VARIANT2_INIT64() \
uint64_t division_result = 0; \
@ -48,7 +53,6 @@
sqrt_result = ctx->state.hs.w[13]; \
} while (0)
#define VARIANT2_2_PORTABLE() \
do \
{ \
@ -96,8 +100,7 @@
chunk2[0] = chunk1_old0 + ((uint64_t*) ctx->c)[0]; \
chunk2[1] = chunk1_old1 + ((uint64_t*) ctx->c)[1]; \
} while (0)
#define VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr) \
((uint64_t*)(b))[0] ^= division_result ^ (sqrt_result << 32); \
{ \
@ -120,13 +123,97 @@
#else
// double precision floating point type is not good enough on current platform
// fall back to the reference code (integer only)
#define VARIANT2_PORTABLE_INTEGER_MATH(b, ptr) \
#define VARIANT2_PORTABLE_INTEGER_MATH(b, ptr) \
do \
{ \
VARIANT2_INTEGER_MATH_DIVISION_STEP(b, ptr); \
VARIANT2_INTEGER_MATH_SQRT_STEP_REF(); \
} while (0)
#endif
// -------------------------------------- VARIANT 4 ---------------------------------------------
struct V4_Instruction code[NUM_INSTRUCTIONS_MAX + 1];
v4_reg r[9];
#define V4_REG_LOAD(dst, src) \
do { \
memcpy((dst), (src), sizeof(v4_reg)); \
*(dst) = (*(dst)); \
} while (0)
#define VARIANT4_RANDOM_MATH_INIT() \
do if (variant >= 4) \
{ \
for (int i = 0; i < 4; ++i) \
V4_REG_LOAD(r + i, (uint8_t*)(ctx->state.hs.w + 12) + sizeof(v4_reg) * i); \
v4_random_math_init(code, height); \
} while (0)
#define VARIANT4_RANDOM_MATH(a, b, r, _b, _b1) \
do if (variant >= 4) \
{ \
uint64_t t[2]; \
memcpy(t, b, sizeof(uint64_t)); \
\
if (sizeof(v4_reg) == sizeof(uint32_t)) \
t[0] ^= ((r[0] + r[1]) | ((uint64_t)(r[2] + r[3]) << 32)); \
else \
t[0] ^= ((r[0] + r[1]) ^ (r[2] + r[3])); \
\
memcpy(b, t, sizeof(uint64_t)); \
\
V4_REG_LOAD(r + 4, a); \
V4_REG_LOAD(r + 5, (uint64_t*)(a) + 1); \
V4_REG_LOAD(r + 6, _b); \
V4_REG_LOAD(r + 7, _b1); \
V4_REG_LOAD(r + 8, (uint64_t*)(_b1) + 1); \
\
v4_random_math(code, r); \
\
memcpy(t, a, sizeof(uint64_t) * 2); \
\
if (sizeof(v4_reg) == sizeof(uint32_t)) { \
t[0] ^= (r[2] | ((uint64_t)(r[3]) << 32)); \
t[1] ^= (r[0] | ((uint64_t)(r[1]) << 32)); \
} else { \
t[0] ^= (r[2] ^ r[3]); \
t[1] ^= (r[0] ^ r[1]); \
} \
memcpy(a, t, sizeof(uint64_t) * 2); \
} while (0)
#define VARIANT4_PORTABLE_SHUFFLE_ADD(out,a_,b_, base_ptr, offset) \
do \
{ \
uint64_t* chunk1 = U64((base_ptr) + ((offset) ^ 0x10)); \
uint64_t* chunk2 = U64((base_ptr) + ((offset) ^ 0x20)); \
uint64_t* chunk3 = U64((base_ptr) + ((offset) ^ 0x30)); \
uint64_t* out_chunk = U64(out);\
\
uint64_t chunk1_old0 = chunk1[0]; \
uint64_t chunk1_old1 = chunk1[1]; \
uint64_t chunk2_old0 = chunk2[0]; \
uint64_t chunk2_old1 = chunk2[1]; \
uint64_t chunk3_old0 = chunk3[0]; \
uint64_t chunk3_old1 = chunk3[1]; \
\
chunk1[0] = chunk3_old0 + ((uint64_t*) ctx->d)[0]; \
chunk1[1] = chunk3_old1 + ((uint64_t*) ctx->d)[1]; \
\
chunk3[0] = chunk2_old0 + ((uint64_t*) a_)[0]; \
chunk3[1] = chunk2_old1 + ((uint64_t*) a_)[1]; \
\
chunk2[0] = chunk1_old0 + ((uint64_t*) b_)[0]; \
chunk2[1] = chunk1_old1 + ((uint64_t*) b_)[1]; \
\
chunk1_old0 ^= chunk2_old0; \
chunk1_old1 ^= chunk2_old1; \
out_chunk[0] ^= chunk3_old0; \
out_chunk[1] ^= chunk3_old1; \
out_chunk[0] ^= chunk1_old0; \
out_chunk[1] ^= chunk1_old1; \
} while (0)
static void xor64(uint8_t *a, const uint64_t b)
{
@ -197,6 +284,7 @@ struct cryptonight_ctx
uint8_t c[AES_BLOCK_SIZE] __attribute__((aligned(16)));
uint8_t d[AES_BLOCK_SIZE] __attribute__((aligned(16)));
uint8_t e[AES_BLOCK_SIZE] __attribute__((aligned(16)));
uint8_t a1[AES_BLOCK_SIZE] __attribute__((aligned(16)));
oaes_ctx *aes_ctx;
};
@ -423,7 +511,7 @@ void SubAndShiftAndMixAddRoundInPlace(uint32_t *temp, uint32_t *AesEncKey)
temp[3] = TestTable2[saved[3]] ^ TestTable3[saved[4]] ^ TestTable4[saved[5]] ^ TestTable1[state[12]] ^ AesEncKey[3];
}
void cryptonight_hash_ctx(void *output, const void *input, size_t len, struct cryptonight_ctx *ctx, int variant)
void cryptonight_hash_ctx(void *output, const void *input, size_t len, struct cryptonight_ctx *ctx, int variant, int height)
{
ctx->aes_ctx = (oaes_ctx *)oaes_alloc();
size_t i, j;
@ -433,6 +521,7 @@ void cryptonight_hash_ctx(void *output, const void *input, size_t len, struct cr
VARIANT1_INIT64();
VARIANT2_INIT64();
VARIANT4_RANDOM_MATH_INIT();
oaes_key_import_data(ctx->aes_ctx, ctx->state.hs.b, AES_KEY_SIZE);
@ -506,7 +595,7 @@ void cryptonight_hash_ctx(void *output, const void *input, size_t len, struct cr
VARIANT1_2(&ctx->long_state[j] + 8);
}
}
else
else if(variant == 2 || variant == 3)
{
for (i = 0; likely(i < ITER / 4); ++i)
{
@ -518,7 +607,7 @@ void cryptonight_hash_ctx(void *output, const void *input, size_t len, struct cr
j = ((uint32_t *)ctx->c)[0] & 0x1FFFF0;
VARIANT2_PORTABLE_INTEGER_MATH(&ctx->long_state[j], ctx->c);
mul64to128(ctx->c, &ctx->long_state[j], ctx->e);
VARIANT2_2_PORTABLE();
VARIANT2_2_PORTABLE();
VARIANT2_PORTABLE_SHUFFLE_ADD1(ctx->long_state, j);
sum_xor_dst(ctx->e, ctx->a, &ctx->long_state[j]);
copy_block(ctx->d, ctx->b);
@ -531,12 +620,43 @@ void cryptonight_hash_ctx(void *output, const void *input, size_t len, struct cr
j = ((uint32_t *)ctx->b)[0] & 0x1FFFF0;
VARIANT2_PORTABLE_INTEGER_MATH(&ctx->long_state[j], ctx->b);
mul64to128(ctx->b, &ctx->long_state[j], ctx->e);
VARIANT2_2_PORTABLE();
VARIANT2_2_PORTABLE();
VARIANT2_PORTABLE_SHUFFLE_ADD2(ctx->long_state, j);
sum_xor_dst(ctx->e, ctx->a, &ctx->long_state[j]);
copy_block(ctx->d, ctx->c);
}
}
else
{
for (i = 0; likely(i < ITER / 4); ++i)
{
j = ((uint32_t *)(ctx->a))[0] & 0x1FFFF0;
SubAndShiftAndMixAddRound((uint32_t *)ctx->c, &ctx->long_state[j], (uint32_t *)ctx->a);
VARIANT4_PORTABLE_SHUFFLE_ADD(ctx->c, ctx->a, ctx->b, ctx->long_state, j);
xor_blocks_dst(ctx->c, ctx->b, &ctx->long_state[j]);
j = ((uint32_t *)ctx->c)[0] & 0x1FFFF0;
copy_block(ctx->a1,ctx->a);
VARIANT4_RANDOM_MATH(ctx->a, &ctx->long_state[j], r, ctx->b, ctx->d);
mul64to128(ctx->c, &ctx->long_state[j], ctx->e);
VARIANT4_PORTABLE_SHUFFLE_ADD(ctx->c, ctx->a1, ctx->b, ctx->long_state, j);
sum_xor_dst(ctx->e, ctx->a, &ctx->long_state[j]);
copy_block(ctx->d, ctx->b);
j = ((uint32_t *)(ctx->a))[0] & 0x1FFFF0;
SubAndShiftAndMixAddRound((uint32_t *)ctx->b, &ctx->long_state[j], (uint32_t *)ctx->a);
VARIANT4_PORTABLE_SHUFFLE_ADD(ctx->b, ctx->a, ctx->c, ctx->long_state, j);
xor_blocks_dst(ctx->b, ctx->c, &ctx->long_state[j]);
j = ((uint32_t *)ctx->b)[0] & 0x1FFFF0;
copy_block(ctx->a1,ctx->a);
VARIANT4_RANDOM_MATH(ctx->a, &ctx->long_state[j], r, ctx->c, ctx->d);
mul64to128(ctx->b, &ctx->long_state[j], ctx->e);
VARIANT4_PORTABLE_SHUFFLE_ADD(ctx->b, ctx->a1, ctx->c, ctx->long_state, j);
sum_xor_dst(ctx->e, ctx->a, &ctx->long_state[j]);
copy_block(ctx->d, ctx->c);
}
}
memcpy(ctx->text, ctx->state.init, INIT_SIZE_BYTE);
@ -672,7 +792,7 @@ void cryptonight_hash_ctx_lite(void *output, const void *input, size_t len, stru
j = ((uint32_t *)ctx->c)[0] & 0x0FFFF0;
VARIANT2_PORTABLE_INTEGER_MATH(&ctx->long_state[j], ctx->c);
mul64to128(ctx->c, &ctx->long_state[j], ctx->e);
VARIANT2_2_PORTABLE();
VARIANT2_2_PORTABLE();
VARIANT2_PORTABLE_SHUFFLE_ADD1(ctx->long_state, j);
sum_xor_dst(ctx->e, ctx->a, &ctx->long_state[j]);
copy_block(ctx->d, ctx->b);
@ -685,7 +805,7 @@ void cryptonight_hash_ctx_lite(void *output, const void *input, size_t len, stru
j = ((uint32_t *)ctx->b)[0] & 0x0FFFF0;
VARIANT2_PORTABLE_INTEGER_MATH(&ctx->long_state[j], ctx->b);
mul64to128(ctx->b, &ctx->long_state[j], ctx->e);
VARIANT2_2_PORTABLE();
VARIANT2_2_PORTABLE();
VARIANT2_PORTABLE_SHUFFLE_ADD2(ctx->long_state, j);
sum_xor_dst(ctx->e, ctx->a, &ctx->long_state[j]);
copy_block(ctx->d, ctx->c);
@ -731,7 +851,7 @@ void cryptonight_hash_ctx_lite(void *output, const void *input, size_t len, stru
oaes_free((OAES_CTX **)&ctx->aes_ctx);
}
void cryptonight(void *output, const void *input, size_t len, int lite, int variant)
void cryptonight(void *output, const void *input, size_t len, int lite, int variant, int height)
{
if(lite)
{
@ -742,8 +862,8 @@ void cryptonight(void *output, const void *input, size_t len, int lite, int vari
else
{
struct cryptonight_ctx *ctx = (struct cryptonight_ctx *)malloc(sizeof(struct cryptonight_ctx));
cryptonight_hash_ctx(output, input, len, ctx, variant);
cryptonight_hash_ctx(output, input, len, ctx, variant, height);
free(ctx);
}
}
}

+ 1
- 1
hash_cn/webassembly/cryptonight.h View File

@ -5,7 +5,7 @@
extern "C" {
#endif
void cryptonight(void *output, const void *input, size_t len, int lite, int variant);
void cryptonight(void *output, const void *input, size_t len, int lite, int variant, int height);
struct cryptonight_ctx;
#ifdef __cplusplus

+ 3
- 7
hash_cn/webassembly/main.c View File

@ -9,7 +9,7 @@
char output[(32 * 2) + 1];
char* hash_cn(char* hex, char* nonce,int lite, int variant)
char* hash_cn(char* hex,int lite, int variant, int height)
{
int len = strlen(hex) / 2;
@ -18,20 +18,16 @@ char* hash_cn(char* hex, char* nonce,int lite, int variant)
char *pos = hex;
for( size_t i = 0; i < len; i++) { sscanf(pos, "%2hhx", &inp[i]); pos += 2; }
pos = nonce;
for(size_t i = 39; i < 43; i++) { sscanf(pos, "%2hhx", &inp[i]); pos += 2; }
unsigned char hash[32];
if(variant == -1)
variant = ((const uint8_t *)inp)[0] >= 7 ? ((const uint8_t *)inp)[0] - 6 : 0;
cryptonight(hash, inp, len, lite, variant);
cryptonight(hash, inp, len, lite, variant, height);
char *ptr = &output[0];
for (size_t i = 0; i < 32; i++) { ptr += sprintf (ptr, "%02x",hash[i]); }
return &output[0];
}
}

+ 26
- 60
hash_cn/webassembly/simple_profile.html View File

@ -2,90 +2,56 @@
<html>
<body>
<button onclick="myFunction()">Click me</button>
<p id="demo"></p>
<script src="cn.js"></script>
<script>
var cn = Module.cwrap('hash_cn', 'string', ['string','string','number','number']);
// https://github.com/SChernykh/monero/blob/5fd83c13fbf8dc304909345e60a853c15b0de1e5/tests/hash/tests-slow-2.txt
//
// 4cf1ff9ca46eb433b36cd9f70e02b14cc06bfd18ca77fa9ccaafd1fd96c674b0 5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374
// 7d292e43f4751714ec07dbcb0e4bbffe2a7afb6066420960684ff57d7474c871 4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e67
// 335563425256edebf1d92dc342369c2f4770ebb4112ba975659bd8a0f210abd0 656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f7265
// 47758e86d2f57210366cec36fff26f9464d89efd116fe6ef28b718b5da120801 657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c
// 48787b48d5c68f0c1dd825c32580af741cc0ee314f08133135c1e86d87a24a95 71756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e697369
// 93bdf47495854f7cfaaca1af8c0f39ef4a3024c10eb0dea23726b0e06ef29e84 757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e20447569732061757465
// a375a71d0541057ccc96719150dfe10b6e6f486b19cf4a0835e19605413a8417 697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c6974
// 163478a76f8f1432533fbdd1284d65c89f37479e54f20841c6ce4eba56c73854 657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e
// 356b0470c6eea75cad7a108179e232905b23bdaf03c2824c6e619d503ee93677 4578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c
// a47e2b007dc25bb279e197a1b91f67ecebe2ddd8791cd32dd2cb76dd21ed943f 73756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e
<!DOCTYPE html>
<html>
<body>
<button onclick="myFunction()">Click me</button>
<p id="demo"></p>
<script src="cn.js"></script>
<button onclick="checkvariants()">Click me</button>
<script>
var cn = Module.cwrap('hash_cn', 'string', ['string','string','number','number']);
var cn = Module.cwrap('hash_cn', 'string', ['string','number','number','number']);
function myFunction2() {
function profile() {
toTest = "5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374";
var t0 = performance.now();
for(i=0;i<100;i++) cn(toTest,toTest.substring(78,86),0,2);
var hash = "0";
for(i=0;i<10;i++) hash=cn(toTest,0,5,1806260);
var t1 = performance.now();
alert("10 cryptonight hashes took " + (t1 - t0) + " milliseconds.")
alert(hash);
}
function myFunction() {
blob = "01111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111113";
alert(cn(blob,blob.substring(78,86),0,0));
function checkvariants() {
blob = "6465206f6d6e69627573206475626974616e64756d";
alert(cn(blob,0,0,0)); // 2f8e3df40bd11f9ac90c743ca8e32bb391da4fb98612aa3b6cdc639ee00b31f5
blob = "01111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111113";
alert(cn(blob,blob.substring(78,86),1,0));
blob = "38274c97c45a172cfc97679870422e3a1ab0784960c60514d816271415c306ee3a3ed1a77e31f6a885c3cb";
alert(cn(blob,0,1,0)); // ed082e49dbd5bbe34a3726a0d1dad981146062b39d36d62c71eb1ed8ab49459b
blob = "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111113";
alert(cn(blob,blob.substring(78,86),0,1));
blob = "5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374";
alert(cn(blob,0,2,0)); // 353fdc068fd47b03c04b9431e005e00b68c2168a3cc7335c8b9b308156591a4f
blob = "11111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111113";
alert(cn(blob,blob.substring(78,86),1,1));
blob = "5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374";
alert(cn(blob,0,3,0)); // 353fdc068fd47b03c04b9431e005e00b68c2168a3cc7335c8b9b308156591a4f
blob = "5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374";
alert(cn(blob,blob.substring(78,86),0,2));
alert(cn(blob,0,4,1806260)); // f759588ad57e758467295443a9bd71490abff8e9dad1b95b6bf2f5d0d78387bc
blob = "5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374";
alert(cn(blob,blob.substring(78,86),1,2));
}
function myFunction3() {
test("5468697320697320612074657374205468697320697320612074657374205468697320697320612074657374");
test("4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e67");
test("656c69742c2073656420646f20656975736d6f642074656d706f7220696e6369646964756e74207574206c61626f7265");
test("657420646f6c6f7265206d61676e6120616c697175612e20557420656e696d206164206d696e696d2076656e69616d2c");
test("71756973206e6f737472756420657865726369746174696f6e20756c6c616d636f206c61626f726973206e697369");
test("757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e20447569732061757465");
test("697275726520646f6c6f7220696e20726570726568656e646572697420696e20766f6c7570746174652076656c6974");
test("657373652063696c6c756d20646f6c6f726520657520667567696174206e756c6c612070617269617475722e");
test("4578636570746575722073696e74206f6363616563617420637570696461746174206e6f6e2070726f6964656e742c");
test("73756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e");
alert(cn(blob,0,5,1806260)); // f759588ad57e758467295443a9bd71490abff8e9dad1b95b6bf2f5d0d78387bc
blob = "4c6f72656d20697073756d20646f6c6f722073697420616d65742c20636f6e73656374657475722061646970697363696e67";
alert(cn(blob,0,5,1806261)); // 5bb833deca2bdd7252a9ccd7b4ce0b6a4854515794b56c207262f7a5b9bdb566
blob = "757420616c697175697020657820656120636f6d6d6f646f20636f6e7365717561742e20447569732061757465";
alert(cn(blob,0,5,1806265)); // 1d290443a4b542af04a82f6b2494a6ee7f20f2754c58e0849032483a56e8e2ef
blob = "73756e7420696e2063756c706120717569206f666669636961206465736572756e74206d6f6c6c697420616e696d20696420657374206c61626f72756d2e";
alert(cn(blob,0,5,1806269)); // 75c6f2ae49a20521de97285b431e717125847fb8935ed84a61e7f8d36a2c3d8e
}
function test(blob) {
hash = cn(blob,blob.substring(78,86),0,2);
alert(hash);
}
</script>

+ 444
- 0
hash_cn/webassembly/variant4_random_math.h View File

@ -0,0 +1,444 @@
#ifndef VARIANT4_RANDOM_MATH_H
#define VARIANT4_RANDOM_MATH_H
// Register size can be configured to either 32 bit (uint32_t) or 64 bit (uint64_t)
typedef uint32_t v4_reg;
enum V4_Settings
{
// Generate code with minimal theoretical latency = 45 cycles, which is equivalent to 15 multiplications
TOTAL_LATENCY = 15 * 3,
// Always generate at least 60 instructions
NUM_INSTRUCTIONS_MIN = 60,
// Never generate more than 70 instructions (final RET instruction doesn't count here)
NUM_INSTRUCTIONS_MAX = 70,
// Available ALUs for MUL
// Modern CPUs typically have only 1 ALU which can do multiplications
ALU_COUNT_MUL = 1,
// Total available ALUs
// Modern CPUs have 4 ALUs, but we use only 3 because random math executes together with other main loop code
ALU_COUNT = 3,
};
enum V4_InstructionList
{
MUL, // a*b
ADD, // a+b + C, C is an unsigned 32-bit constant
SUB, // a-b
ROR, // rotate right "a" by "b & 31" bits
ROL, // rotate left "a" by "b & 31" bits
XOR, // a^b
RET, // finish execution
V4_INSTRUCTION_COUNT = RET,
};
// V4_InstructionDefinition is used to generate code from random data
// Every random sequence of bytes is a valid code
//
// There are 9 registers in total:
// - 4 variable registers
// - 5 constant registers initialized from loop variables
// This is why dst_index is 2 bits
enum V4_InstructionDefinition
{
V4_OPCODE_BITS = 3,
V4_DST_INDEX_BITS = 2,
V4_SRC_INDEX_BITS = 3,
};
// uint32 seems to be faster
struct V4_Instruction
{
uint8_t dst_index;
uint8_t src_index;
uint8_t opcode;
uint32_t C;
};
#ifndef FORCEINLINE
#if defined(__GNUC__)
#define FORCEINLINE __attribute__((always_inline)) inline
#elif defined(_MSC_VER)
#define FORCEINLINE __forceinline
#else
#define FORCEINLINE inline
#endif
#endif
#ifndef UNREACHABLE_CODE
#if defined(__GNUC__)
#define UNREACHABLE_CODE __builtin_unreachable()
#elif defined(_MSC_VER)
#define UNREACHABLE_CODE __assume(false)
#else
#define UNREACHABLE_CODE
#endif
#endif
// Random math interpreter's loop is fully unrolled and inlined to achieve 100% branch prediction on CPU:
// every switch-case will point to the same destination on every iteration of Cryptonight main loop
//
// This is about as fast as it can get without using low-level machine code generation
static FORCEINLINE void v4_random_math(const struct V4_Instruction* code, v4_reg* r)
{
enum
{
REG_BITS = sizeof(v4_reg) * 8,
};
#define V4_EXEC(i) \
{ \
const struct V4_Instruction* op = code + i; \
const v4_reg src = r[op->src_index]; \
v4_reg* dst = r + op->dst_index; \
switch (op->opcode) \
{ \
case MUL: \
*dst *= src; \
break; \
case ADD: \
*dst += src + op->C; \
break; \
case SUB: \
*dst -= src; \
break; \
case ROR: \
{ \
const uint32_t shift = src % REG_BITS; \
*dst = (*dst >> shift) | (*dst << ((REG_BITS - shift) % REG_BITS)); \
} \
break; \
case ROL: \
{ \
const uint32_t shift = src % REG_BITS; \
*dst = (*dst << shift) | (*dst >> ((REG_BITS - shift) % REG_BITS)); \
} \
break; \
case XOR: \
*dst ^= src; \
break; \
case RET: \
return; \
default: \
UNREACHABLE_CODE; \
break; \
} \
}
#define V4_EXEC_10(j) \
V4_EXEC(j + 0) \
V4_EXEC(j + 1) \
V4_EXEC(j + 2) \
V4_EXEC(j + 3) \
V4_EXEC(j + 4) \
V4_EXEC(j + 5) \
V4_EXEC(j + 6) \
V4_EXEC(j + 7) \
V4_EXEC(j + 8) \
V4_EXEC(j + 9)
// Generated program can have 60 + a few more (usually 2-3) instructions to achieve required latency
// I've checked all block heights < 10,000,000 and here is the distribution of program sizes:
//
// 60 27960
// 61 105054
// 62 2452759
// 63 5115997
// 64 1022269
// 65 1109635
// 66 153145
// 67 8550
// 68 4529
// 69 102
// Unroll 70 instructions here
V4_EXEC_10(0); // instructions 0-9
V4_EXEC_10(10); // instructions 10-19
V4_EXEC_10(20); // instructions 20-29
V4_EXEC_10(30); // instructions 30-39
V4_EXEC_10(40); // instructions 40-49
V4_EXEC_10(50); // instructions 50-59
V4_EXEC_10(60); // instructions 60-69
#undef V4_EXEC_10
#undef V4_EXEC
}
// If we don't have enough data available, generate more
static FORCEINLINE void check_data(size_t* data_index, const size_t bytes_needed, int8_t* data, const size_t data_size)
{
if (*data_index + bytes_needed > data_size)
{
//hash_extra_blake(data, data_size, (char*) data);
blake((uint8_t *) data,data_size,(uint8_t *) data);
*data_index = 0;
}
}
// Generates as many random math operations as possible with given latency and ALU restrictions
// "code" array must have space for NUM_INSTRUCTIONS_MAX+1 instructions
static inline int v4_random_math_init(struct V4_Instruction* code, const uint64_t height)
{
// MUL is 3 cycles, 3-way addition and rotations are 2 cycles, SUB/XOR are 1 cycle
// These latencies match real-life instruction latencies for Intel CPUs starting from Sandy Bridge and up to Skylake/Coffee lake
//
// AMD Ryzen has the same latencies except 1-cycle ROR/ROL, so it'll be a bit faster than Intel Sandy Bridge and newer processors
// Surprisingly, Intel Nehalem also has 1-cycle ROR/ROL, so it'll also be faster than Intel Sandy Bridge and newer processors
// AMD Bulldozer has 4 cycles latency for MUL (slower than Intel) and 1 cycle for ROR/ROL (faster than Intel), so average performance will be the same
// Source: https://www.agner.org/optimize/instruction_tables.pdf
const int op_latency[V4_INSTRUCTION_COUNT] = { 3, 2, 1, 2, 2, 1 };
// Instruction latencies for theoretical ASIC implementation
const int asic_op_latency[V4_INSTRUCTION_COUNT] = { 3, 1, 1, 1, 1, 1 };
// Available ALUs for each instruction
const int op_ALUs[V4_INSTRUCTION_COUNT] = { ALU_COUNT_MUL, ALU_COUNT, ALU_COUNT, ALU_COUNT, ALU_COUNT, ALU_COUNT };
int8_t data[32];
memset(data, 0, sizeof(data));
uint64_t tmp = (height);
memcpy(data, &tmp, sizeof(uint64_t));
data[20] = 0xda; // change seed
// Set data_index past the last byte in data
// to trigger full data update with blake hash
// before we start using it
size_t data_index = sizeof(data);
int code_size;
// There is a small chance (1.8%) that register R8 won't be used in the generated program
// So we keep track of it and try again if it's not used
bool r8_used;
do {
int latency[9];
int asic_latency[9];
// Tracks previous instruction and value of the source operand for registers R0-R3 throughout code execution
// byte 0: current value of the destination register
// byte 1: instruction opcode
// byte 2: current value of the source register
//
// Registers R4-R8 are constant and are treated as having the same value because when we do
// the same operation twice with two constant source registers, it can be optimized into a single operation
uint32_t inst_data[9] = { 0, 1, 2, 3, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF, 0xFFFFFF };
bool alu_busy[TOTAL_LATENCY + 1][ALU_COUNT];
bool is_rotation[V4_INSTRUCTION_COUNT];
bool rotated[4];
int rotate_count = 0;
memset(latency, 0, sizeof(latency));
memset(asic_latency, 0, sizeof(asic_latency));
memset(alu_busy, 0, sizeof(alu_busy));
memset(is_rotation, 0, sizeof(is_rotation));
memset(rotated, 0, sizeof(rotated));
is_rotation[ROR] = true;
is_rotation[ROL] = true;
int num_retries = 0;
code_size = 0;
int total_iterations = 0;
r8_used = false;
// Generate random code to achieve minimal required latency for our abstract CPU
// Try to get this latency for all 4 registers
while (((latency[0] < TOTAL_LATENCY) || (latency[1] < TOTAL_LATENCY) || (latency[2] < TOTAL_LATENCY) || (latency[3] < TOTAL_LATENCY)) && (num_retries < 64))
{
// Fail-safe to guarantee loop termination
++total_iterations;
if (total_iterations > 256)
break;
check_data(&data_index, 1, data, sizeof(data));
const uint8_t c = ((uint8_t*)data)[data_index++];
// MUL = opcodes 0-2
// ADD = opcode 3
// SUB = opcode 4
// ROR/ROL = opcode 5, shift direction is selected randomly
// XOR = opcodes 6-7
uint8_t opcode = c & ((1 << V4_OPCODE_BITS) - 1);
if (opcode == 5)
{
check_data(&data_index, 1, data, sizeof(data));
opcode = (data[data_index++] >= 0) ? ROR : ROL;
}
else if (opcode >= 6)
{
opcode = XOR;
}
else
{
opcode = (opcode <= 2) ? MUL : (opcode - 2);
}
uint8_t dst_index = (c >> V4_OPCODE_BITS) & ((1 << V4_DST_INDEX_BITS) - 1);
uint8_t src_index = (c >> (V4_OPCODE_BITS + V4_DST_INDEX_BITS)) & ((1 << V4_SRC_INDEX_BITS) - 1);
const int a = dst_index;
int b = src_index;
// Don't do ADD/SUB/XOR with the same register
if (((opcode == ADD) || (opcode == SUB) || (opcode == XOR)) && (a == b))
{
// Use register R8 as source instead
b = 8;
src_index = 8;
}
// Don't do rotation with the same destination twice because it's equal to a single rotation
if (is_rotation[opcode] && rotated[a])
{
continue;
}
// Don't do the same instruction (except MUL) with the same source value twice because all other cases can be optimized:
// 2xADD(a, b, C) = ADD(a, b*2, C1+C2), same for SUB and rotations
// 2xXOR(a, b) = NOP
if ((opcode != MUL) && ((inst_data[a] & 0xFFFF00) == (opcode << 8) + ((inst_data[b] & 255) << 16)))
{
continue;
}
// Find which ALU is available (and when) for this instruction
int next_latency = (latency[a] > latency[b]) ? latency[a] : latency[b];
int alu_index = -1;
while (next_latency < TOTAL_LATENCY)
{
for (int i = op_ALUs[opcode] - 1; i >= 0; --i)
{
if (!alu_busy[next_latency][i])
{
// ADD is implemented as two 1-cycle instructions on a real CPU, so do an additional availability check
if ((opcode == ADD) && alu_busy[next_latency + 1][i])
{
continue;
}
// Rotation can only start when previous rotation is finished, so do an additional availability check
if (is_rotation[opcode] && (next_latency < rotate_count * op_latency[opcode]))
{
continue;
}
alu_index = i;
break;
}
}
if (alu_index >= 0)
{
break;
}
++next_latency;
}
// Don't generate instructions that leave some register unchanged for more than 7 cycles
if (next_latency > latency[a] + 7)
{
continue;
}
next_latency += op_latency[opcode];
if (next_latency <= TOTAL_LATENCY)
{
if (is_rotation[opcode])
{
++rotate_count;
}
// Mark ALU as busy only for the first cycle when it starts executing the instruction because ALUs are fully pipelined
alu_busy[next_latency - op_latency[opcode]][alu_index] = true;
latency[a] = next_latency;
// ASIC is supposed to have enough ALUs to run as many independent instructions per cycle as possible, so latency calculation for ASIC is simple
asic_latency[a] = ((asic_latency[a] > asic_latency[b]) ? asic_latency[a] : asic_latency[b]) + asic_op_latency[opcode];
rotated[a] = is_rotation[opcode];
inst_data[a] = code_size + (opcode << 8) + ((inst_data[b] & 255) << 16);
code[code_size].opcode = opcode;
code[code_size].dst_index = dst_index;
code[code_size].src_index = src_index;
code[code_size].C = 0;
if (src_index == 8)
{
r8_used = true;
}
if (opcode == ADD)
{
// ADD instruction is implemented as two 1-cycle instructions on a real CPU, so mark ALU as busy for the next cycle too
alu_busy[next_latency - op_latency[opcode] + 1][alu_index] = true;
// ADD instruction requires 4 more random bytes for 32-bit constant "C" in "a = a + b + C"
check_data(&data_index, sizeof(uint32_t), data, sizeof(data));
uint32_t t;
memcpy(&t, data + data_index, sizeof(uint32_t));
code[code_size].C = (t);
data_index += sizeof(uint32_t);
}
++code_size;
if (code_size >= NUM_INSTRUCTIONS_MIN)
{
break;
}
}
else
{
++num_retries;
}
}
// ASIC has more execution resources and can extract as much parallelism from the code as possible
// We need to add a few more MUL and ROR instructions to achieve minimal required latency for ASIC
// Get this latency for at least 1 of the 4 registers
const int prev_code_size = code_size;
while ((code_size < NUM_INSTRUCTIONS_MAX) && (asic_latency[0] < TOTAL_LATENCY) && (asic_latency[1] < TOTAL_LATENCY) && (asic_latency[2] < TOTAL_LATENCY) && (asic_latency[3] < TOTAL_LATENCY))
{
int min_idx = 0;
int max_idx = 0;
for (int i = 1; i < 4; ++i)
{
if (asic_latency[i] < asic_latency[min_idx]) min_idx = i;
if (asic_latency[i] > asic_latency[max_idx]) max_idx = i;
}
const uint8_t pattern[3] = { ROR, MUL, MUL };
const uint8_t opcode = pattern[(code_size - prev_code_size) % 3];
latency[min_idx] = latency[max_idx] + op_latency[opcode];
asic_latency[min_idx] = asic_latency[max_idx] + asic_op_latency[opcode];
code[code_size].opcode = opcode;
code[code_size].dst_index = min_idx;
code[code_size].src_index = max_idx;
code[code_size].C = 0;
++code_size;
}
// There is ~98.15% chance that loop condition is false, so this loop will execute only 1 iteration most of the time
// It never does more than 4 iterations for all block heights < 10,000,000
} while (!r8_used || (code_size < NUM_INSTRUCTIONS_MIN) || (code_size > NUM_INSTRUCTIONS_MAX));
// It's guaranteed that NUM_INSTRUCTIONS_MIN <= code_size <= NUM_INSTRUCTIONS_MAX here
// Add final instruction to stop the interpreter
code[code_size].opcode = RET;
code[code_size].dst_index = 0;
code[code_size].src_index = 0;
code[code_size].C = 0;
return code_size;
}
#endif

+ 0
- 1
server/Server/PoolConnection.cs View File

@ -60,7 +60,6 @@ namespace Server
public string DefaultAlgorithm = "cn";
public int DefaultVariant = -1;
public CcHashset<Client> WebClients = new CcHashset<Client>();
public void Send(Client client, string msg)

Loading…
Cancel
Save