From 7782c58d1455c39aa37c114c9f7bc383b85c7250 Mon Sep 17 00:00:00 2001 From: Beaudan Date: Fri, 7 Dec 2018 13:50:41 +1100 Subject: [PATCH] Slight refactor of PoW file to make easier to test plus some tests for the PoW functionality --- Gruntfile.js | 1 - libloki/proof-of-work.js | 60 +++++++----- libloki/test/.eslintrc | 10 +- libloki/test/_test.js | 12 --- .../test/in_memory_signal_protocol_store.js | 3 +- test/app/proof-of-work_test.js | 97 +++++++++++++++++++ 6 files changed, 135 insertions(+), 48 deletions(-) create mode 100644 test/app/proof-of-work_test.js diff --git a/Gruntfile.js b/Gruntfile.js index a97bf9860..5b521683f 100644 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -462,7 +462,6 @@ module.exports = grunt => { ]); grunt.registerTask('dev', ['default', 'watch']); grunt.registerTask('test', ['unit-tests', 'lib-unit-tests', 'loki-unit-tests']); - grunt.registerTask('test-loki', ['loki-unit-tests']); grunt.registerTask('date', ['gitinfo', 'getExpireTime']); grunt.registerTask('default', [ 'exec:build-protobuf', diff --git a/libloki/proof-of-work.js b/libloki/proof-of-work.js index 494c4987a..0381d70a8 100644 --- a/libloki/proof-of-work.js +++ b/libloki/proof-of-work.js @@ -2,9 +2,17 @@ const hash = require('js-sha512'); const bb = require('bytebuffer'); const { BigInteger } = require('jsbn'); +module.exports = { + calcTarget, + incrementNonce, + bufferToBase64, + bigIntToUint8Array, + greaterThan, +}; + const NONCE_LEN = 8; // Modify this value for difficulty scaling -let NONCE_TRIALS = 1000; +let NONCE_TRIALS = 10; // Increment Uint8Array nonce by 1 with carrying function incrementNonce(nonce) { @@ -62,27 +70,7 @@ function calcPoW(timestamp, ttl, pubKey, data) { bb.wrap(timestamp.toString() + ttl.toString() + pubKey + data, 'binary').toArrayBuffer() ); - // 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); + const target = calcTarget(ttl, payload.length); let nonce = new Uint8Array(NONCE_LEN); let trialValue = bigIntToUint8Array( @@ -105,10 +93,34 @@ function calcPoW(timestamp, ttl, pubKey, data) { return bufferToBase64(nonce); } +function calcTarget(ttl, payloadLen) { + // payloadLength + NONCE_LEN + const totalLen = new BigInteger(payloadLen.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); + return bigIntToUint8Array(targetNum); +} + // Start calculation in child process when main process sends message data process.on('message', msg => { - if (msg.development) - NONCE_TRIALS = 10; + if (!msg.development) + NONCE_TRIALS = 1000; process.send({ nonce: calcPoW( msg.timestamp, diff --git a/libloki/test/.eslintrc b/libloki/test/.eslintrc index 29971976c..194818b3a 100644 --- a/libloki/test/.eslintrc +++ b/libloki/test/.eslintrc @@ -13,14 +13,6 @@ }, "globals": { "assert": true, - "assertEqualArrayBuffers": true, - "dcodeIO": true, - "getString": true, - "hexToArrayBuffer": true, - "MockServer": true, - "MockSocket": true, - "clearDatabase": true, - "PROTO_ROOT": true, - "stringToArrayBuffer": true + "clearDatabase": true } } diff --git a/libloki/test/_test.js b/libloki/test/_test.js index ef33c6a91..e26c8ab40 100644 --- a/libloki/test/_test.js +++ b/libloki/test/_test.js @@ -49,18 +49,6 @@ Whisper.Database.id = 'test'; /* * global helpers for tests */ -window.assertEqualArrayBuffers = (ab1, ab2) => { - assert.deepEqual(new Uint8Array(ab1), new Uint8Array(ab2)); -}; - -window.hexToArrayBuffer = str => { - const ret = new ArrayBuffer(str.length / 2); - const array = new Uint8Array(ret); - for (let i = 0; i < str.length / 2; i += 1) - array[i] = parseInt(str.substr(i * 2, 2), 16); - return ret; -}; - window.clearDatabase = async () => { await window.Signal.Data.removeAll(); }; diff --git a/libtextsecure/test/in_memory_signal_protocol_store.js b/libtextsecure/test/in_memory_signal_protocol_store.js index 397a82ff5..dcdb0d01a 100644 --- a/libtextsecure/test/in_memory_signal_protocol_store.js +++ b/libtextsecure/test/in_memory_signal_protocol_store.js @@ -75,7 +75,7 @@ SignalProtocolStore.prototype = { resolve(res); }); }, - storePreKey(keyId, keyPair, contactPubKey= null) { + storePreKey(keyId, keyPair, contactPubKey = null) { if (contactPubKey) { const data = { id: keyId, @@ -177,7 +177,6 @@ SignalProtocolStore.prototype = { }, async storeContactSignedPreKey(pubKey, signedPreKey) { const key = { - // id: (autoincrement) identityKeyString: pubKey, keyId: signedPreKey.keyId, publicKey: signedPreKey.publicKey, diff --git a/test/app/proof-of-work_test.js b/test/app/proof-of-work_test.js new file mode 100644 index 000000000..311380548 --- /dev/null +++ b/test/app/proof-of-work_test.js @@ -0,0 +1,97 @@ +/* global require */ +const { assert } = require('chai'); +const { BigInteger } = require('jsbn'); + +const { + calcTarget, + incrementNonce, + bufferToBase64, + bigIntToUint8Array, + greaterThan, +} = require('../../libloki/proof-of-work'); + +describe('Proof of Work Worker', () => { + it('should increment a Uint8Array nonce correctly', () => { + const arr1Before = new Uint8Array([0,0,0,0,0,0,0,0]); + const arr1After = incrementNonce(arr1Before); + assert.strictEqual(arr1After[0], 0); + assert.strictEqual(arr1After[1], 0); + assert.strictEqual(arr1After[2], 0); + assert.strictEqual(arr1After[3], 0); + assert.strictEqual(arr1After[4], 0); + assert.strictEqual(arr1After[5], 0); + assert.strictEqual(arr1After[6], 0); + assert.strictEqual(arr1After[7], 1); + }); + + it('should increment a Uint8Array nonce correctly', () => { + let arr = new Uint8Array([0,0,0,0,0,0,0,0]); + assert.deepEqual(incrementNonce(arr), new Uint8Array([0,0,0,0,0,0,0,1])); + arr = new Uint8Array([0,0,0,0,0,0,0,0]); + for(let i = 0; i <= 255; i += 1) { + arr = incrementNonce(arr); + } + assert.deepEqual(arr, new Uint8Array([0,0,0,0,0,0,1,0])); + arr = new Uint8Array([255,255,255,255,255,255,255,255]); + assert.deepEqual(incrementNonce(arr), new Uint8Array([0,0,0,0,0,0,0,0])); + }); + + it('should calculate a correct difficulty target', () => { + // These values will need to be updated if we adjust the difficulty settings + let payloadLen = 625; + const ttl = 86400; + let expectedTarget = new Uint8Array([0,4,119,164,35,224,222,64]); + + let actualTarget = calcTarget(ttl, payloadLen); + assert.deepEqual(actualTarget, expectedTarget); + payloadLen = 6597; + expectedTarget = new Uint8Array([0,0,109,145,174,146,124,3]); + actualTarget = calcTarget(ttl, payloadLen); + assert.deepEqual(actualTarget, expectedTarget); + }); + + it('should correclty compare two Uint8Arrays', () => { + let arr1 = new Uint8Array([0,0,0,0,0,0,0,0,0,1]); + let arr2 = new Uint8Array([0,0,0,0,0,0,0,0,0,1]); + assert.isFalse(greaterThan(arr1, arr2)) + arr1 = new Uint8Array([0,0,0,0,0,0,0,0,0,2]); + arr2 = new Uint8Array([0,0,0,0,0,0,0,0,0,1]); + assert.isTrue(greaterThan(arr1, arr2)) + arr1 = new Uint8Array([255,255,255,255,255,255,255,255,255,255]); + arr2 = new Uint8Array([255,255,255,255,255,255,255,255,255,254]); + assert.isTrue(greaterThan(arr1, arr2)) + arr1 = new Uint8Array([254,255,255,255,255,255,255,255,255,255]); + arr2 = new Uint8Array([255,255,255,255,255,255,255,255,255,255]); + assert.isFalse(greaterThan(arr1, arr2)); + arr1 = new Uint8Array([0]); + arr2 = new Uint8Array([0,0]); + assert.isFalse(greaterThan(arr1, arr2)) + }); + + it('should correclty convert a Uint8Array to a base64 string', () => { + let arr = new Uint8Array([1,2,3]); + let expected = 'AQID'; + assert.strictEqual(bufferToBase64(arr), expected); + arr = new Uint8Array([123,25,3,121,45,87,24,111]); + expected = 'exkDeS1XGG8='; + assert.strictEqual(bufferToBase64(arr), expected); + arr = new Uint8Array([]); + expected = ''; + assert.strictEqual(bufferToBase64(arr), expected); + }); + + it('should correclty convert a BigInteger to a Uint8Array', () => { + let bigInt = new BigInteger(Number.MAX_SAFE_INTEGER.toString()); + let expected = new Uint8Array([0, 31, 255, 255, 255, 255, 255, 255]); + assert.deepEqual(bigIntToUint8Array(bigInt), expected); + bigInt = new BigInteger('0'); + expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 0]); + assert.deepEqual(bigIntToUint8Array(bigInt), expected); + bigInt = new BigInteger('255'); + expected = new Uint8Array([0, 0, 0, 0, 0, 0, 0, 255]); + assert.deepEqual(bigIntToUint8Array(bigInt), expected); + bigInt = new BigInteger('256'); + expected = new Uint8Array([0, 0, 0, 0, 0, 0, 1, 0]); + assert.deepEqual(bigIntToUint8Array(bigInt), expected); + }); +});