Skip to content

Commit

Permalink
feat: add work validation function
Browse files Browse the repository at this point in the history
Also, previously, the generated work was not actually valid
  • Loading branch information
marvinroger committed Feb 13, 2018
1 parent e5ab317 commit 3376387
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 23 deletions.
16 changes: 15 additions & 1 deletion src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@ import Native from '../native'

let instance = null
let _generateWork = null
let _validateWork = null
export function init () {
return new Promise((resolve, reject) => {
try {
Native().then(native => {
instance = native
_generateWork = instance.cwrap('emscripten_generate_work', 'string', ['string', 'number', 'number'])
_validateWork = instance.cwrap('emscripten_validate_work', 'number', ['string', 'string'])
resolve()
})
} catch (err) {
Expand All @@ -16,10 +18,22 @@ export function init () {
})
}

const checkNotInitialized = () => {
if (instance === null) throw new Error('NanoCurrency is not initialized')
}

export function generateWork (blockHash, workerNumber = 0, workerCount = 1) {
if (instance === null) throw new Error('Nano is not initialized')
checkNotInitialized()

const work = _generateWork(blockHash, workerNumber, workerCount)

return work !== '0000000000000000' ? work : null
}

export function validateWork (blockHash, work) {
checkNotInitialized()

const valid = _validateWork(blockHash, work) === 1

return valid
}
63 changes: 44 additions & 19 deletions src/native/functions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@

#include "blake2/ref/blake2.h"

const uint64_t WORK_THRESHOLD = 0xffffffc000000000;
const uint8_t BLOCK_HASH_LENGTH = 32;
const uint8_t WORK_LENGTH = 8;
const uint64_t MIN_UINT64 = 0x0000000000000000;
const uint64_t MAX_UINT64 = 0xffffffffffffffff;
const uint64_t WORK_THRESHOLD = 0xffffffc000000000;

void hex_to_bytes(const std::string& hex, uint8_t* bytes) {
int byte_index = 0;
Expand All @@ -17,46 +19,69 @@ void hex_to_bytes(const std::string& hex, uint8_t* bytes) {
}
}

void reverse_bytes(uint8_t* bytes, uint8_t length) {
for (int i = 0; i < (length / 2); i++) {
uint8_t temp = bytes[i];
bytes[i] = bytes[(length - 1) - i];
bytes[(length - 1) - i] = temp;
}
}

bool validate_work(uint8_t* block_hash, uint8_t* work) {
blake2b_state hash;
uint64_t output = 0;

blake2b_init(&hash, sizeof (output));
blake2b_update(&hash, work, WORK_LENGTH);
blake2b_update(&hash, block_hash, BLOCK_HASH_LENGTH);
blake2b_final(&hash, reinterpret_cast <uint8_t*> (&output), sizeof (output));

return output >= WORK_THRESHOLD;
}

uint64_t generate_work(uint8_t* block_hash, uint8_t worker_number, uint8_t worker_count) {
const uint64_t interval = (MAX_UINT64 - MIN_UINT64) / worker_count;

const uint64_t lower_bound = MIN_UINT64 + (worker_number * interval);
const uint64_t upper_bound = (worker_number != worker_count - 1) ? lower_bound + interval : MAX_UINT64;

blake2b_state hash;
uint64_t work = lower_bound;
uint64_t output = 0;

while(output < WORK_THRESHOLD) {
while (true) {
if (work == upper_bound) return 0;
blake2b_init(&hash, sizeof (output));
blake2b_update(&hash, reinterpret_cast <uint8_t*> (&work), sizeof (work));
blake2b_update(&hash, block_hash, sizeof (block_hash));
blake2b_final(&hash, reinterpret_cast <uint8_t*> (&output), sizeof (output));

if (validate_work(block_hash, reinterpret_cast <uint8_t*> (&work))) return work;

work++;
}
}

extern "C" {
EMSCRIPTEN_KEEPALIVE
uint8_t emscripten_validate_work(char* block_hash_hex, char* work_hex) {
std::string block_hash_hex_string(block_hash_hex);
uint8_t block_hash_bytes[BLOCK_HASH_LENGTH];
hex_to_bytes(block_hash_hex_string, block_hash_bytes);

work--;
std::string work_hex_string(work_hex);
uint8_t work_bytes[WORK_LENGTH];
hex_to_bytes(work_hex_string, work_bytes);
reverse_bytes(work_bytes, WORK_LENGTH);

return work;
}
bool valid = validate_work(block_hash_bytes, work_bytes);

uint64_t swap_uint64(uint64_t val) {
val = ((val << 8) & 0xFF00FF00FF00FF00ULL ) | ((val >> 8) & 0x00FF00FF00FF00FFULL );
val = ((val << 16) & 0xFFFF0000FFFF0000ULL ) | ((val >> 16) & 0x0000FFFF0000FFFFULL );
return (val << 32) | (val >> 32);
}
return valid ? 1 : 0;
}

extern "C" {
EMSCRIPTEN_KEEPALIVE
char* emscripten_generate_work(char* block_hash_hex, uint8_t worker_number, uint8_t worker_count) {
std::string block_hash_hex_string(block_hash_hex);
uint8_t block_hash_bytes[32];
uint8_t block_hash_bytes[BLOCK_HASH_LENGTH];
hex_to_bytes(block_hash_hex_string, block_hash_bytes);

uint64_t work = generate_work(block_hash_bytes, worker_number, worker_count);
char work_hex[16 + 1];
sprintf(work_hex, "%016llx", swap_uint64(work));
sprintf(work_hex, "%016llx", work);

return strdup(work_hex);
}
Expand Down
22 changes: 19 additions & 3 deletions test/test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,27 @@ const assert = require('assert')

const nano = require('../dist/nanocurrency.cjs')

const VALID = {
hash: '7f7122e843b27524f4f1d6bd14aefd1c8f01d36ae8653d37417533c0d4bc2be6',
work: '0000000000995bc3'
}

const INVALID = {
hash: '3ed191ec702f384514ba35e1f9081148df5a9ab48fe0f604b6e5b9f7177cee32',
work: 'bb6737f2daf01a2c'
}

async function test () {
await nano.init()

const work = nano.generateWork('7f7122e843b27524f4f1d6bd14aefd1c8f01d36ae8653d37417533c0d4bc2be6')
assert.deepEqual(work, '81df2c0600000000')

const work = nano.generateWork(VALID.hash)
assert.deepEqual(work, VALID.work)

const valid = nano.validateWork(VALID.hash, VALID.work)
assert.deepEqual(valid, true)

const invalid = nano.validateWork(INVALID.hash, INVALID.work)
assert.deepEqual(invalid, false)
}

test()

0 comments on commit 3376387

Please sign in to comment.