Skip to content

Commit

Permalink
Balloon algo (#38)
Browse files Browse the repository at this point in the history
with missing balloonhash() func and many other fixes...

to be tested on windows...

Signed-off-by: Tanguy Pruvot <[email protected]>
  • Loading branch information
tpruvot committed Mar 16, 2019
1 parent 0d7a97a commit cab5dca
Show file tree
Hide file tree
Showing 10 changed files with 419 additions and 5 deletions.
2 changes: 2 additions & 0 deletions Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand All @@ -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 \
Expand Down
50 changes: 50 additions & 0 deletions algo/balloon.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#include "miner.h"
#include <string.h>
#include <stdint.h>

#include <openssl/sha.h>

#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;
}
2 changes: 0 additions & 2 deletions algo/geek.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
#include <sha3/sph_hamsi.h>
#include <sha3/sph_simd.h>

//#define DEBUG_ALGO

void geekhash(void *output, const void *input)
{
sph_blake512_context ctx_blake;
Expand Down
286 changes: 286 additions & 0 deletions balloon/balloon-algo.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
#include <stdbool.h>
#include <stdint.h>
#include <stddef.h>
#include <string.h>

#include <openssl/aes.h>
#include <openssl/evp.h>
#include <openssl/sha.h>

#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
Loading

0 comments on commit cab5dca

Please sign in to comment.