From 4598fd222a3423bb77f19bdf936cd355cc8c2a08 Mon Sep 17 00:00:00 2001 From: Beaudan Date: Wed, 31 Oct 2018 16:08:46 +1100 Subject: [PATCH] Cleaned up newNonce stuff. Now doing target calculation with BigIntegers --- js/modules/loki_message_api.js | 4 +-- libloki/proof-of-work.js | 66 +++++++++++++++++----------------- 2 files changed, 36 insertions(+), 34 deletions(-) diff --git a/js/modules/loki_message_api.js b/js/modules/loki_message_api.js index 44d291d5c..283f86bae 100644 --- a/js/modules/loki_message_api.js +++ b/js/modules/loki_message_api.js @@ -30,7 +30,7 @@ function initialize({ url }) { timestamp, ttl, pubKey, - data, + data: Array.from(data), }); // Handle child process error (should never happen) @@ -133,4 +133,4 @@ function HTTPError(message, providedCode, response, stack) { e.response = response; } return e; -} \ No newline at end of file +} diff --git a/libloki/proof-of-work.js b/libloki/proof-of-work.js index 1c633e510..fd1057d6a 100644 --- a/libloki/proof-of-work.js +++ b/libloki/proof-of-work.js @@ -1,19 +1,22 @@ const hash = require('js-sha512'); const bb = require('bytebuffer'); +const BigInteger = require('jsbn').BigInteger; const NONCE_LEN = 8; +// Modify this value for difficulty scaling +const NONCE_TRIALS = 1000; // Increment Uint8Array nonce by 1 with carrying function incrementNonce(nonce) { let idx = NONCE_LEN - 1; - const newNonce = nonce; + const newNonce = new Uint8Array(nonce); newNonce[idx] += 1; // Nonce will just reset to 0 if all values are 255 causing infinite loop - while (nonce[idx] === 0 && idx > 0) { + while (newNonce[idx] === 0 && idx > 0) { idx -= 1; newNonce[idx] += 1; } - return nonce; + return newNonce; } // Convert a Uint8Array to a base64 string @@ -25,27 +28,27 @@ function bufferToBase64(buf) { return bb.btoa(binaryString); } -// Convert javascript number to Uint8Array of length 8 -function numberToUintArr(numberVal) { +// Convert BigInteger to Uint8Array of length NONCE_LEN +function bigIntToUint8Array(bigInt) { const arr = new Uint8Array(NONCE_LEN); let n; for (let idx = NONCE_LEN - 1; idx >= 0; idx -= 1) { n = NONCE_LEN - (idx + 1); // 256 ** n is the value of one bit in arr[idx], modulus to carry over - arr[idx] = (numberVal / 256**n) % 256; + // (bigInt / 256**n) % 256; + const uint8Val = (bigInt.divide((new BigInteger('256')).pow(n))).mod(new BigInteger('256')); + arr[idx] = uint8Val.intValue(); } return arr; } // Compare two Uint8Arrays, return true if arr1 is > arr2 -function compareUint8Arrays(arr1, arr2) { +function greaterThan(arr1, arr2) { // Early exit if lengths are not equal. Should never happen if (arr1.length !== arr2.length) return false; - const len = arr1.length - - for (let i = 0; i < len; i += 1) { + for (let i = 0, len = arr1.length; i < len; i += 1) { if (arr1[i] > arr2[i]) return true; if (arr1[i] < arr2[i]) @@ -63,40 +66,39 @@ function calcPoW(timestamp, ttl, pubKey, data) { const payload = new Uint8Array(leadingArray.length + data.length); payload.set(leadingArray); payload.set(data, leadingArray.length); - // Modify this value for difficulty scaling - const nonceTrialsPerByte = 1000; + + // payloadLength + NONCE_LEN + const totalLen = (new BigInteger(payload.length.toString())).add(new BigInteger(NONCE_LEN.toString())); + // ttl * totalLen + const ttlMult = (new BigInteger(ttl.toString())).multiply(totalLen); + // ttlMult / (2^16 - 1) + const innerFrac = ttlMult.divide((new BigInteger('2').pow(16)).subtract(new BigInteger('1'))); + // totalLen + innerFrac + const lenPlusInnerFrac = totalLen.add(innerFrac); + // NONCE_TRIALS * lenPlusInnerFrac + const denominator = (new BigInteger(NONCE_TRIALS.toString())).multiply(lenPlusInnerFrac); + // 2^64 - 1 + const two64 = (new BigInteger('2').pow(64)).subtract(new BigInteger('1')); + // two64 / denominator + const targetNum = two64.divide(denominator); + const target = bigIntToUint8Array(targetNum); + let nonce = new Uint8Array(NONCE_LEN); - let trialValue = numberToUintArr(Number.MAX_SAFE_INTEGER); - // Target is converted to Uint8Array for simple comparison with trialValue - const targetNum = Math.floor(2**64 / ( - nonceTrialsPerByte * ( - payload.length + NONCE_LEN + ( - (ttl * ( payload.length + NONCE_LEN )) / - 2**16 - ) - ) - )); - const target = numberToUintArr(targetNum); + let trialValue = bigIntToUint8Array(new BigInteger(Number.MAX_SAFE_INTEGER.toString())); const initialHash = new Uint8Array(bb.wrap(hash(payload), 'hex').toArrayBuffer()); const innerPayload = new Uint8Array(initialHash.length + NONCE_LEN); innerPayload.set(initialHash, NONCE_LEN); let resultHash; - while (compareUint8Arrays(trialValue, target)) { + while (greaterThan(trialValue, target)) { nonce = incrementNonce(nonce); innerPayload.set(nonce); resultHash = hash(innerPayload); - trialValue = (new Uint8Array(bb.wrap(resultHash, 'hex').toArrayBuffer())).slice(0, 8); + trialValue = (new Uint8Array(bb.wrap(resultHash, 'hex').toArrayBuffer())).slice(0, NONCE_LEN); } return bufferToBase64(nonce); } // Start calculation in child process when main process sends message data process.on('message', (msg) => { - // Convert data back to Uint8Array after IPC has serialised to JSON - const msgLen = Object.keys(msg.data).length; - const msgData = new Uint8Array(msgLen); - for (let i = 0; i < msgLen; i += 1) { - msgData[i] = msg.data[i]; - } - process.send({nonce: calcPoW(msg.timestamp, msg.ttl, msg.pubKey, msgData)}); + process.send({nonce: calcPoW(msg.timestamp, msg.ttl, msg.pubKey, new Uint8Array(msg.data))}); });