diff --git a/src/index.js b/src/index.js index 298cc03..37d5051 100644 --- a/src/index.js +++ b/src/index.js @@ -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) { @@ -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 +} diff --git a/src/native/functions.cpp b/src/native/functions.cpp index b64eae5..a00098a 100644 --- a/src/native/functions.cpp +++ b/src/native/functions.cpp @@ -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; @@ -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 (&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 (&work), sizeof (work)); - blake2b_update(&hash, block_hash, sizeof (block_hash)); - blake2b_final(&hash, reinterpret_cast (&output), sizeof (output)); + + if (validate_work(block_hash, reinterpret_cast (&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); } diff --git a/test/test.js b/test/test.js index 5d6d69e..e9ba099 100644 --- a/test/test.js +++ b/test/test.js @@ -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()