From cab5dcae5ce47acd77c0b22d358abf750598a131 Mon Sep 17 00:00:00 2001 From: Tanguy Pruvot Date: Sat, 16 Mar 2019 13:42:28 +0100 Subject: [PATCH] Balloon algo (#38) with missing balloonhash() func and many other fixes... to be tested on windows... Signed-off-by: Tanguy Pruvot --- Makefile.am | 2 + algo/balloon.c | 50 +++++++ algo/geek.c | 2 - balloon/balloon-algo.c | 286 +++++++++++++++++++++++++++++++++++++++ balloon/balloon.h | 57 ++++++++ cpu-miner.c | 11 ++ cpuminer.vcxproj | 2 + cpuminer.vcxproj.filters | 9 +- miner.h | 2 + util.c | 3 + 10 files changed, 419 insertions(+), 5 deletions(-) create mode 100644 algo/balloon.c create mode 100644 balloon/balloon-algo.c create mode 100644 balloon/balloon.h diff --git a/Makefile.am b/Makefile.am index dd9bc7496..38be55417 100644 --- a/Makefile.am +++ b/Makefile.am @@ -43,6 +43,7 @@ cpuminer_SOURCES = \ sha3/sph_shabal.c \ sha3/sph_whirlpool.c \ sha3/gost_streebog.c \ + balloon/balloon-algo.c \ crypto/blake2s.c \ crypto/blake2b.c \ crypto/oaes_lib.c \ @@ -58,6 +59,7 @@ cpuminer_SOURCES = \ yescrypt/sha256_Y.c \ algo/allium.c \ algo/axiom.c \ + algo/balloon.c \ algo/bastion.c \ algo/blake.c \ algo/blakecoin.c \ diff --git a/algo/balloon.c b/algo/balloon.c new file mode 100644 index 000000000..c99b5c943 --- /dev/null +++ b/algo/balloon.c @@ -0,0 +1,50 @@ +#include "miner.h" +#include +#include + +#include + +#include "balloon/balloon.h" + +void balloonhash(void *output, const void *input) +{ + balloon_reset(); + // input 80, cost 128 / 4 + balloon_128((unsigned char *)input, (unsigned char *)output); +} + +int scanhash_balloon(int thr_id, struct work *work, uint32_t max_nonce, uint64_t *hashes_done) +{ + uint32_t _ALIGN(128) hash32[8]; + uint32_t _ALIGN(128) endiandata[20]; + uint32_t *pdata = work->data; + uint32_t *ptarget = work->target; + + const uint32_t Htarg = ptarget[7]; + const uint32_t first_nonce = pdata[19]; + + uint32_t n = first_nonce; + + for (int i=0; i < 19; i++) { + be32enc(&endiandata[i], pdata[i]); + }; + + balloon_reset(); + do { + be32enc(&endiandata[19], n); + balloon_128((unsigned char *)endiandata, (unsigned char *)hash32); + if (hash32[7] < Htarg && fulltest(hash32, ptarget)) { + work_set_target_ratio(work, hash32); + *hashes_done = n - first_nonce + 1; + pdata[19] = n; + return true; + } + n++; + + } while (n < max_nonce && !work_restart[thr_id].restart); + + *hashes_done = n - first_nonce + 1; + pdata[19] = n; + + return 0; +} diff --git a/algo/geek.c b/algo/geek.c index 7abf3f6eb..c52b83fd1 100644 --- a/algo/geek.c +++ b/algo/geek.c @@ -15,8 +15,6 @@ #include #include -//#define DEBUG_ALGO - void geekhash(void *output, const void *input) { sph_blake512_context ctx_blake; diff --git a/balloon/balloon-algo.c b/balloon/balloon-algo.c new file mode 100644 index 000000000..9046af5d2 --- /dev/null +++ b/balloon/balloon-algo.c @@ -0,0 +1,286 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "balloon.h" + +#define MIN(a, b) (((a) < (b)) ? (a) : (b)) + +#ifdef __cplusplus +extern "C"{ +#endif + +static void bitstream_init(struct bitstream *b) +{ + SHA256_Init(&b->c); + b->initialized = false; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + b->ctx = EVP_CIPHER_CTX_new(); + EVP_CIPHER_CTX_init(b->ctx); +#else + EVP_CIPHER_CTX_init(&b->ctx); +#endif + b->zeros = malloc(BITSTREAM_BUF_SIZE * sizeof(uint8_t)); + memset(b->zeros, 0, BITSTREAM_BUF_SIZE); +} + +static void bitstream_free(struct bitstream *b) +{ + uint8_t out[AES_BLOCK_SIZE]; + int outl; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + EVP_EncryptFinal(b->ctx, out, &outl); + EVP_CIPHER_CTX_free(b->ctx); +#else + EVP_EncryptFinal(&b->ctx, out, &outl); + EVP_CIPHER_CTX_cleanup(&b->ctx); +#endif + free(b->zeros); +} + +static void bitstream_seed_add(struct bitstream *b, const void *seed, size_t seedlen) { + SHA256_Update(&b->c, seed, seedlen); +} + +static void bitstream_seed_finalize(struct bitstream *b) +{ + uint8_t key_bytes[SHA256_DIGEST_LENGTH]; + SHA256_Final(key_bytes, &b->c); + uint8_t iv[AES_BLOCK_SIZE]; + memset(iv, 0, AES_BLOCK_SIZE); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + EVP_CIPHER_CTX_set_padding(b->ctx, 1); + EVP_EncryptInit(b->ctx, EVP_aes_128_ctr(), key_bytes, iv); +#else + EVP_CIPHER_CTX_set_padding(&b->ctx, 1); + EVP_EncryptInit(&b->ctx, EVP_aes_128_ctr(), key_bytes, iv); +#endif + b->initialized = true; +} + +static __inline void encrypt_partial(struct bitstream *b, void *outp, int to_encrypt) +{ + int encl; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + EVP_EncryptUpdate(b->ctx, outp, &encl, b->zeros, to_encrypt); +#else + EVP_EncryptUpdate(&b->ctx, outp, &encl, b->zeros, to_encrypt); +#endif +} + +static void bitstream_fill_buffer(struct bitstream *b, void *out, size_t outlen) +{ + size_t total = 0; + while (total < outlen) { + const int to_encrypt = MIN(outlen - total, BITSTREAM_BUF_SIZE); + encrypt_partial(b, (char *)out + total, to_encrypt); + total += to_encrypt; + } +} + +static void compress(uint64_t *counter, uint8_t *out, const uint8_t *blocks[], size_t blocks_to_comp) +{ + SHA256_CTX ctx; + SHA256_Init(&ctx); + SHA256_Update(&ctx, counter, 8); + for (unsigned int i = 0; i < blocks_to_comp; i++) + SHA256_Update(&ctx, blocks[i], BLOCK_SIZE); + SHA256_Final(out, &ctx); + *counter += 1; +} + +static void expand(uint64_t *counter, uint8_t *buf, size_t blocks_in_buf) +{ + uint8_t *blocks[1] = { buf }; + uint8_t *cur = buf + BLOCK_SIZE; + for (size_t i = 1; i < blocks_in_buf; i++) { + compress(counter, cur, (const uint8_t **) blocks, 1); + blocks[0] += BLOCK_SIZE; + cur += BLOCK_SIZE; + } +} + +static uint64_t bytes_to_littleend_uint64(const uint8_t *bytes, size_t n_bytes) +{ + if (n_bytes > 8) + n_bytes = 8; + uint64_t out = 0; + for (int i = n_bytes-1; i >= 0; i--) { + out <<= 8; + out |= bytes[i]; + } + return out; +} + +static __inline void bytes_to_littleend8_uint64(const uint8_t *bytes, uint64_t *out) +{ + *out <<= 8; + *out |= *(bytes + 7); + *out <<= 8; + *out |= *(bytes + 6); + *out <<= 8; + *out |= *(bytes + 5); + *out <<= 8; + *out |= *(bytes + 4); + *out <<= 8; + *out |= *(bytes + 3); + *out <<= 8; + *out |= *(bytes + 2); + *out <<= 8; + *out |= *(bytes + 1); + *out <<= 8; + *out |= *(bytes + 0); +} + +static void * block_index(const struct hash_state *s, size_t i) { + return s->buffer + (BLOCK_SIZE * i); +} + +static uint64_t options_n_blocks(const struct balloon_options *opts) +{ + const uint32_t bsize = BLOCK_SIZE; + uint64_t ret = (opts->s_cost * 1024) / bsize; + return (ret < BLOCKS_MIN) ? BLOCKS_MIN : ret; +} + +static void * block_last(const struct hash_state *s) { + return block_index(s, s->n_blocks - 1); +} + +static void hash_state_init(struct hash_state *s, const struct balloon_options *opts, const uint8_t salt[SALT_LEN]) +{ + s->counter = 0; + s->n_blocks = options_n_blocks(opts); + if (s->n_blocks % 2 != 0) s->n_blocks++; + s->has_mixed = false; + s->opts = opts; + s->buffer = malloc(s->n_blocks * BLOCK_SIZE); + int a = salt[0]; + a++; + bitstream_init(&s->bstream); + bitstream_seed_add(&s->bstream, salt, SALT_LEN); + bitstream_seed_add(&s->bstream, &opts->s_cost, 8); + bitstream_seed_add(&s->bstream, &opts->t_cost, 4); + bitstream_seed_finalize(&s->bstream); +} + +static void hash_state_free(struct hash_state *s) { + bitstream_free(&s->bstream); + free(s->buffer); +} + +static void hash_state_fill(struct hash_state *s, const uint8_t salt[SALT_LEN], const uint8_t *in, size_t inlen) +{ + SHA256_CTX c; + SHA256_Init(&c); + SHA256_Update(&c, &s->counter, 8); + SHA256_Update(&c, salt, SALT_LEN); + SHA256_Update(&c, in, inlen); + SHA256_Update(&c, &s->opts->s_cost, 8); + SHA256_Update(&c, &s->opts->t_cost, 4); + SHA256_Final(s->buffer, &c); + s->counter++; + expand(&s->counter, s->buffer, s->n_blocks); +} + +// TODO: common for all or different data per thread ? +uint8_t prebuf[409600]; +uint64_t prebuf_le[409600 / 8]; +uint8_t prebuf_filled = 0; + +static void hash_state_mix(struct hash_state *s, int32_t mixrounds) +{ + if (!prebuf_filled) { + bitstream_fill_buffer(&s->bstream, prebuf, 409600); + prebuf_filled = 1; + uint8_t *buf = prebuf; + uint64_t *lebuf = prebuf_le; + for (int i = 0; i < 409600; i+=8) { + bytes_to_littleend8_uint64(buf, lebuf); + *lebuf %= 4096; + lebuf++; + buf += 8; + } + } + uint64_t *buf = prebuf_le; + uint8_t *sbuf = s->buffer; + + uint64_t neighbor; + int32_t n_blocks = s->n_blocks; + uint8_t *last_block = (sbuf + (BLOCK_SIZE*(n_blocks-1))); + for (int32_t rounds=0; rounds < mixrounds; rounds++) { + uint8_t *cur_block = sbuf; + uint8_t *blocks[5]; + uint8_t **block = blocks; + { // i = 0 + *(block++) = last_block; + *(block++) = cur_block; + *(block++) = (sbuf + (BLOCK_SIZE * (*(buf++)))); + *(block++) = (sbuf + (BLOCK_SIZE * (*(buf++)))); + *(block++) = (sbuf + (BLOCK_SIZE * (*(buf++)))); + + compress(&s->counter, cur_block, (const uint8_t**) blocks, 5); + cur_block += BLOCK_SIZE; + } + for (size_t i = 1; i < n_blocks; i++) { + block = blocks; + *(block++) = cur_block - BLOCK_SIZE; + *(block++) = cur_block; + *(block++) = (sbuf + (BLOCK_SIZE * (*(buf++)))); + *(block++) = (sbuf + (BLOCK_SIZE * (*(buf++)))); + *(block++) = (sbuf + (BLOCK_SIZE * (*(buf++)))); + + compress(&s->counter, cur_block, (const uint8_t**) blocks, 5); + cur_block += BLOCK_SIZE; + } + s->has_mixed = true; + } +} + +static void hash_state_extract(const struct hash_state *s, uint8_t out[BLOCK_SIZE]) +{ + uint8_t *b = block_last(s); + memcpy((char *)out, (const char *)b, BLOCK_SIZE); +} + + +static void balloon_init(struct balloon_options *opts, int64_t s_cost, int32_t t_cost) +{ + opts->s_cost = s_cost; + opts->t_cost = t_cost; +} + +void balloon(unsigned char *input, unsigned char *output, int32_t len, int64_t s_cost, int32_t t_cost) +{ + struct balloon_options opts; + struct hash_state s; + balloon_init(&opts, s_cost, t_cost); + hash_state_init(&s, &opts, input); + hash_state_fill(&s, input, input, len); + hash_state_mix(&s, t_cost); + hash_state_extract(&s, output); + hash_state_free(&s); +} + +void balloon_128(unsigned char *input, unsigned char *output) +{ + balloon(input, output, 80, 128, 4); +} + +void balloon_hash(unsigned char *input, unsigned char *output, int64_t s_cost, int32_t t_cost) +{ + balloon(input, output, 80, s_cost, t_cost); +} + +void balloon_reset() { + prebuf_filled = 0; +} + +#ifdef __cplusplus +} +#endif diff --git a/balloon/balloon.h b/balloon/balloon.h new file mode 100644 index 000000000..439e453e1 --- /dev/null +++ b/balloon/balloon.h @@ -0,0 +1,57 @@ +#include +#include +#include +#include +#include + +#ifdef __cplusplus +extern "C" { +#endif + +#define BITSTREAM_BUF_SIZE ((32) * (AES_BLOCK_SIZE)) +#define N_NEIGHBORS (3) +#define SALT_LEN (32) +#define INLEN_MAX (1ull<<20) +#define TCOST_MIN 1ull +#define SCOST_MIN (1) +#define SCOST_MAX (UINT32_MAX) +#define BLOCKS_MIN (1ull) +#define THREADS_MAX 4096 +#define BLOCK_SIZE (32) +#define UNUSED __attribute__ ((unused)) + +struct bitstream { + bool initialized; + uint8_t *zeros; + SHA256_CTX c; +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + EVP_CIPHER_CTX* ctx; +#else + EVP_CIPHER_CTX ctx; +#endif +}; + +struct hash_state; + +struct hash_state { + uint64_t counter; + uint64_t n_blocks; + bool has_mixed; + uint8_t *buffer; + struct bitstream bstream; + const struct balloon_options *opts; +}; + +struct balloon_options { + int64_t s_cost; + int32_t t_cost; +}; + +void balloon_128(unsigned char *input, unsigned char *output); +void balloon_hash(unsigned char *input, unsigned char *output, int64_t s_cost, int32_t t_cost); +void balloon(unsigned char *input, unsigned char *output, int32_t len, int64_t s_cost, int32_t t_cost); +void balloon_reset(); + +#ifdef __cplusplus +} +#endif diff --git a/cpu-miner.c b/cpu-miner.c index 018d5ec41..4068512e6 100644 --- a/cpu-miner.c +++ b/cpu-miner.c @@ -84,6 +84,7 @@ enum algos { ALGO_QUARK, /* Quark */ ALGO_ALLIUM, /* Garlicoin double lyra2 */ ALGO_AXIOM, /* Shabal 256 Memohash */ + ALGO_BALLOON, ALGO_BASTION, ALGO_BLAKE, /* Blake 256 */ ALGO_BLAKECOIN, /* Simplified 8 rounds Blake 256 */ @@ -152,6 +153,7 @@ static const char *algo_names[] = { "quark", "allium", "axiom", + "balloon", "bastion", "blake", "blakecoin", @@ -315,6 +317,7 @@ Options:\n\ -a, --algo=ALGO specify the algorithm to use\n\ allium Garlicoin double lyra2\n\ axiom Shabal-256 MemoHash\n\ + balloon Balloon\n\ bitcore Timetravel with 10 algos\n\ blake Blake-256 14-rounds (SFR)\n\ blakecoin Blake-256 single sha256 merkle\n\ @@ -1081,6 +1084,7 @@ static int share_result(int result, struct work *work, const char *reason) switch (opt_algo) { case ALGO_AXIOM: + case ALGO_BALLOON: case ALGO_CRYPTOLIGHT: case ALGO_CRYPTONIGHT: case ALGO_PLUCK: @@ -2178,6 +2182,7 @@ static void *miner_thread(void *userdata) max64 = 0xF; break; case ALGO_AXIOM: + case ALGO_BALLOON: case ALGO_CRYPTOLIGHT: case ALGO_CRYPTONIGHT: case ALGO_SCRYPTJANE: @@ -2265,6 +2270,9 @@ static void *miner_thread(void *userdata) case ALGO_AXIOM: rc = scanhash_axiom(thr_id, &work, max_nonce, &hashes_done); break; + case ALGO_BALLOON: + rc = scanhash_balloon(thr_id, &work, max_nonce, &hashes_done); + break; case ALGO_BASTION: rc = scanhash_bastion(thr_id, &work, max_nonce, &hashes_done); break; @@ -2465,10 +2473,12 @@ static void *miner_thread(void *userdata) if (!opt_quiet && (time(NULL) - tm_rate_log) > opt_maxlograte) { switch(opt_algo) { case ALGO_AXIOM: + case ALGO_BALLOON: case ALGO_CRYPTOLIGHT: case ALGO_CRYPTONIGHT: case ALGO_PLUCK: case ALGO_SCRYPTJANE: + case ALGO_YESCRYPT: applog(LOG_INFO, "CPU #%d: %.2f H/s", thr_id, thr_hashrates[thr_id]); break; default: @@ -2488,6 +2498,7 @@ static void *miner_thread(void *userdata) case ALGO_CRYPTOLIGHT: case ALGO_CRYPTONIGHT: case ALGO_AXIOM: + case ALGO_BALLOON: case ALGO_SCRYPTJANE: sprintf(s, "%.3f", hashrate); applog(LOG_NOTICE, "Total: %s H/s", s); diff --git a/cpuminer.vcxproj b/cpuminer.vcxproj index a881b2a2c..80ccd7fcb 100644 --- a/cpuminer.vcxproj +++ b/cpuminer.vcxproj @@ -185,6 +185,7 @@ + @@ -204,6 +205,7 @@ + diff --git a/cpuminer.vcxproj.filters b/cpuminer.vcxproj.filters index 43ec1414f..c68cc204e 100644 --- a/cpuminer.vcxproj.filters +++ b/cpuminer.vcxproj.filters @@ -183,6 +183,12 @@ algo + + algo + + + algo + algo @@ -345,9 +351,6 @@ algo - - algo - algo diff --git a/miner.h b/miner.h index 912ae8aa0..1739ee009 100644 --- a/miner.h +++ b/miner.h @@ -200,6 +200,7 @@ struct work; int scanhash_allium(int thr_id, struct work *work, uint32_t max_nonce, uint64_t *hashes_done); int scanhash_axiom(int thr_id, struct work *work, uint32_t max_nonce, uint64_t *hashes_done); +int scanhash_balloon(int thr_id, struct work *work, uint32_t max_nonce, uint64_t *hashes_done); int scanhash_bastion(int thr_id, struct work *work, uint32_t max_nonce, uint64_t *hashes_done); int scanhash_blake(int thr_id, struct work *work, uint32_t max_nonce, uint64_t *hashes_done); int scanhash_blakecoin(int thr_id, struct work *work, uint32_t max_nonce, uint64_t *hashes_done); @@ -502,6 +503,7 @@ void print_hash_tests(void); void sha256d(unsigned char *hash, const unsigned char *data, int len); void allium_hash(void *state, const void *input); void axiomhash(void *state, const void *input); +void balloonhash(void *output, const void *input); void bastionhash(void *output, const void *input); void blakehash(void *state, const void *input); void blakecoinhash(void *state, const void *input); diff --git a/util.c b/util.c index 648016df1..3a29ff9e3 100644 --- a/util.c +++ b/util.c @@ -2332,6 +2332,9 @@ void print_hash_tests(void) axiomhash(&hash[0], &buf[0]); printpfx("axiom", hash); + balloonhash(&hash[0], &buf[0]); + printpfx("balloon", hash); + bastionhash(&hash[0], &buf[0]); printpfx("bastion", hash);