From e520bf109a180fd4dc9ddc1f74de934624de827b Mon Sep 17 00:00:00 2001 From: Beaudan Date: Thu, 30 May 2019 15:53:41 +1000 Subject: [PATCH] Persist PoW difficulty and update if get response from snodes --- config/default.json | 1 + js/background.js | 5 ++++ js/modules/loki_message_api.js | 5 ++++ js/modules/loki_rpc.js | 35 ++++++++++++++++++++----- js/util_worker_tasks.js | 4 +-- libloki/proof-of-work.js | 18 ++++++------- libloki/test/metrics.js | 48 +++++++++++++++------------------- libtextsecure/errors.js | 14 ++++++++++ main.js | 1 + preload.js | 1 + 10 files changed, 88 insertions(+), 44 deletions(-) diff --git a/config/default.json b/config/default.json index e642583f7..8429f2a80 100644 --- a/config/default.json +++ b/config/default.json @@ -5,6 +5,7 @@ "contentProxyUrl": "random.snode", "localServerPort": "8081", "snodeServerPort": "8080", + "defaultPoWDifficulty": "10", "disableAutoUpdate": false, "updatesUrl": "https://updates2.signal.org/desktop", "updatesPublicKey": diff --git a/js/background.js b/js/background.js index 76ae88606..6bdb08222 100644 --- a/js/background.js +++ b/js/background.js @@ -233,6 +233,11 @@ window.libloki.api.sendOnlineBroadcastMessage(pubKey, isPing); }); + const currentPoWDifficulty = storage.get('PoWDifficulty', null); + if (!currentPoWDifficulty) { + storage.put('PoWDifficulty', window.getDefaultPoWDifficulty()); + } + // These make key operations available to IPC handlers created in preload.js window.Events = { getDeviceName: () => textsecure.storage.user.getDeviceName(), diff --git a/js/modules/loki_message_api.js b/js/modules/loki_message_api.js index b5e0b9ffe..6ec02c117 100644 --- a/js/modules/loki_message_api.js +++ b/js/modules/loki_message_api.js @@ -164,6 +164,11 @@ class LokiMessageAPI { await lokiSnodeAPI.updateSwarmNodes(params.pubKey, newSwarm); this.sendingSwarmNodes[params.timestamp] = newSwarm; return false; + } else if (e instanceof textsecure.WrongDifficultyError) { + const { newDifficulty } = e; + if (!Number.isNaN(newDifficulty)) { + window.storage.put('PoWDifficulty', newDifficulty); + } } else if (e instanceof textsecure.NotFoundError) { // TODO: Handle resolution error successiveFailures += 1; diff --git a/js/modules/loki_rpc.js b/js/modules/loki_rpc.js index a61422506..7cc3c45b2 100644 --- a/js/modules/loki_rpc.js +++ b/js/modules/loki_rpc.js @@ -40,23 +40,46 @@ const fetch = async (url, options = {}) => { }); if (response.status === 421) { - let newSwarm = await response.text(); + let responseJson = await response.text(); + let newSwarm = []; if (doEncryptChannel) { try { - newSwarm = await libloki.crypto.snodeCipher.decrypt( + responseJson = await libloki.crypto.snodeCipher.decrypt( address, - newSwarm + responseJson ); } catch (e) { log.warn(`Could not decrypt response from ${address}`, e); } + } + try { + responseJson = responseJson === '' ? {} : JSON.parse(responseJson); + newSwarm = responseJson.snodes ? responseJson.snodes : []; + } catch (e) { + log.warn(`Could not parse string to json ${newSwarm}`, e); + } + throw new textsecure.WrongSwarmError(newSwarm); + } + + if (response.status === 402) { + let responseJson = await response.text(); + if (doEncryptChannel) { try { - newSwarm = newSwarm === '' ? {} : JSON.parse(newSwarm); + responseJson = await libloki.crypto.snodeCipher.decrypt( + address, + responseJson + ); } catch (e) { - log.warn(`Could not parse string to json ${newSwarm}`, e); + log.warn(`Could not decrypt response from ${address}`, e); } } - throw new textsecure.WrongSwarmError(newSwarm); + try { + responseJson = responseJson === '' ? {} : JSON.parse(responseJson); + } catch (e) { + log.warn(`Could not parse string to json ${responseJson}`, e); + } + const newDifficulty = parseInt(responseJson.difficulty, 10); + throw new textsecure.WrongDifficultyError(newDifficulty); } if (!response.ok) { diff --git a/js/util_worker_tasks.js b/js/util_worker_tasks.js index 446ee7da8..b084e7b1a 100644 --- a/js/util_worker_tasks.js +++ b/js/util_worker_tasks.js @@ -47,7 +47,7 @@ function calcPoW( pubKey, data, development, - nonceTrials = undefined, + difficulty = undefined, increment = 1, startNonce = 0 ) { @@ -57,7 +57,7 @@ function calcPoW( pubKey, data, development, - nonceTrials, + difficulty, increment, startNonce ); diff --git a/libloki/proof-of-work.js b/libloki/proof-of-work.js index 039e2395d..cdd95dba4 100644 --- a/libloki/proof-of-work.js +++ b/libloki/proof-of-work.js @@ -1,8 +1,8 @@ /* global dcodeIO, crypto, JSBI */ const NONCE_LEN = 8; // Modify this value for difficulty scaling -const DEV_NONCE_TRIALS = 10; -const PROD_NONCE_TRIALS = 100; +const DEV_DIFFICULTY = 10; +const PROD_DIFFICULTY = 100; const pow = { // Increment Uint8Array nonce by '_increment' with carrying @@ -63,7 +63,7 @@ const pow = { pubKey, data, development = false, - _nonceTrials = null, + _difficulty = null, increment = 1, startNonce = 0 ) { @@ -74,9 +74,9 @@ const pow = { ).toArrayBuffer() ); - const nonceTrials = - _nonceTrials || (development ? DEV_NONCE_TRIALS : PROD_NONCE_TRIALS); - const target = pow.calcTarget(ttl, payload.length, nonceTrials); + const difficulty = + _difficulty || (development ? DEV_DIFFICULTY : PROD_DIFFICULTY); + const target = pow.calcTarget(ttl, payload.length, difficulty); let nonce = new Uint8Array(NONCE_LEN); nonce = pow.incrementNonce(nonce, startNonce); // initial value @@ -103,7 +103,7 @@ const pow = { return pow.bufferToBase64(nonce); }, - calcTarget(ttl, payloadLen, nonceTrials = PROD_NONCE_TRIALS) { + calcTarget(ttl, payloadLen, difficulty = PROD_DIFFICULTY) { // payloadLength + NONCE_LEN const totalLen = JSBI.add(JSBI.BigInt(payloadLen), JSBI.BigInt(NONCE_LEN)); // ttl converted to seconds @@ -119,9 +119,9 @@ const pow = { const innerFrac = JSBI.divide(ttlMult, two16); // totalLen + innerFrac const lenPlusInnerFrac = JSBI.add(totalLen, innerFrac); - // nonceTrials * lenPlusInnerFrac + // difficulty * lenPlusInnerFrac const denominator = JSBI.multiply( - JSBI.BigInt(nonceTrials), + JSBI.BigInt(difficulty), lenPlusInnerFrac ); // 2^64 - 1 diff --git a/libloki/test/metrics.js b/libloki/test/metrics.js index cdd49059d..222177c52 100644 --- a/libloki/test/metrics.js +++ b/libloki/test/metrics.js @@ -3,7 +3,7 @@ let jobId = 0; let currentTrace = 0; let plotlyDiv; const workers = []; -async function run(messageLength, numWorkers = 1, nonceTrials = 100, ttl = 72) { +async function run(messageLength, numWorkers = 1, difficulty = 100, ttl = 72) { const timestamp = Math.floor(Date.now() / 1000); const pubKey = '05ec8635a07a13743516c7c9b3412f3e8252efb7fcaf67eb1615ffba62bebc6802'; @@ -29,7 +29,7 @@ async function run(messageLength, numWorkers = 1, nonceTrials = 100, ttl = 72) { pubKey, data, false, - nonceTrials, + difficulty, increment, index, ]); @@ -50,12 +50,12 @@ async function run(messageLength, numWorkers = 1, nonceTrials = 100, ttl = 72) { async function runPoW({ iteration, - nonceTrials, + difficulty, numWorkers, messageLength = 50, ttl = 72, }) { - const name = `W:${numWorkers} - NT: ${nonceTrials} - L:${messageLength} - TTL:${ttl}`; + const name = `W:${numWorkers} - NT: ${difficulty} - L:${messageLength} - TTL:${ttl}`; Plotly.addTraces(plotlyDiv, { y: [], type: 'box', @@ -64,7 +64,7 @@ async function runPoW({ }); for (let i = 0; i < iteration; i += 1) { // eslint-disable-next-line no-await-in-loop - await run(messageLength, numWorkers, nonceTrials, ttl); + await run(messageLength, numWorkers, difficulty, ttl); } currentTrace += 1; @@ -86,9 +86,7 @@ function addPoint(duration) { } async function startMessageLengthRun() { const iteration0 = parseFloat(document.getElementById('iteration0').value); - const nonceTrials0 = parseFloat( - document.getElementById('nonceTrials0').value - ); + const difficulty0 = parseFloat(document.getElementById('difficulty0').value); const numWorkers0 = parseFloat(document.getElementById('numWorkers0').value); const messageLengthStart0 = parseFloat( document.getElementById('messageLengthStart0').value @@ -108,7 +106,7 @@ async function startMessageLengthRun() { // eslint-disable-next-line no-await-in-loop await runPoW({ iteration: iteration0, - nonceTrials: nonceTrials0, + difficulty: difficulty0, numWorkers: numWorkers0, messageLength: l, ttl: TTL0, @@ -117,9 +115,7 @@ async function startMessageLengthRun() { } async function startNumWorkerRun() { const iteration1 = parseFloat(document.getElementById('iteration1').value); - const nonceTrials1 = parseFloat( - document.getElementById('nonceTrials1').value - ); + const difficulty1 = parseFloat(document.getElementById('difficulty1').value); const numWorkersStart1 = parseFloat( document.getElementById('numWorkersStart1').value ); @@ -138,34 +134,34 @@ async function startNumWorkerRun() { // eslint-disable-next-line no-await-in-loop await runPoW({ iteration: iteration1, - nonceTrials: nonceTrials1, + difficulty: difficulty1, numWorkers, messageLength: messageLength1, ttl: TTL1, }); } } -async function startNonceTrialsRun() { +async function startDifficultyRun() { const iteration2 = parseFloat(document.getElementById('iteration2').value); const messageLength2 = parseFloat( document.getElementById('messageLength2').value ); const numWorkers2 = parseFloat(document.getElementById('numWorkers2').value); - const nonceTrialsStart2 = parseFloat( - document.getElementById('nonceTrialsStart2').value + const difficultyStart2 = parseFloat( + document.getElementById('difficultyStart2').value ); - const nonceTrialsStop2 = parseFloat( - document.getElementById('nonceTrialsStop2').value + const difficultyStop2 = parseFloat( + document.getElementById('difficultyStop2').value ); - const nonceTrialsStep2 = parseFloat( - document.getElementById('nonceTrialsStep2').value + const difficultyStep2 = parseFloat( + document.getElementById('difficultyStep2').value ); const TTL2 = parseFloat(document.getElementById('TTL2').value); - for (let n = nonceTrialsStart2; n < nonceTrialsStop2; n += nonceTrialsStep2) { + for (let n = difficultyStart2; n < difficultyStop2; n += difficultyStep2) { // eslint-disable-next-line no-await-in-loop await runPoW({ iteration: iteration2, - nonceTrials: n, + difficulty: n, numWorkers: numWorkers2, messageLength: messageLength2, ttl: TTL2, @@ -174,9 +170,7 @@ async function startNonceTrialsRun() { } async function starTTLRun() { const iteration3 = parseFloat(document.getElementById('iteration3').value); - const nonceTrials3 = parseFloat( - document.getElementById('nonceTrials3').value - ); + const difficulty3 = parseFloat(document.getElementById('difficulty3').value); const messageLength3 = parseFloat( document.getElementById('messageLength3').value ); @@ -188,7 +182,7 @@ async function starTTLRun() { // eslint-disable-next-line no-await-in-loop await runPoW({ iteration: iteration3, - nonceTrials: nonceTrials3, + difficulty: difficulty3, numWorkers: numWorkers3, messageLength: messageLength3, ttl, @@ -216,7 +210,7 @@ async function start(index) { await startNumWorkerRun(); break; case 2: - await startNonceTrialsRun(); + await startDifficultyRun(); break; case 3: await starTTLRun(); diff --git a/libtextsecure/errors.js b/libtextsecure/errors.js index 78f9fcfb1..0ad331080 100644 --- a/libtextsecure/errors.js +++ b/libtextsecure/errors.js @@ -222,6 +222,19 @@ } } + function WrongDifficultyError(newDifficulty) { + this.name = 'WrongDifficultyError'; + this.newDifficulty = newDifficulty; + + Error.call(this, this.name); + + // Maintains proper stack trace, where our error was thrown (only available on V8) + // via https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Error + if (Error.captureStackTrace) { + Error.captureStackTrace(this); + } + } + window.textsecure.UnregisteredUserError = UnregisteredUserError; window.textsecure.SendMessageNetworkError = SendMessageNetworkError; window.textsecure.IncomingIdentityKeyError = IncomingIdentityKeyError; @@ -237,4 +250,5 @@ window.textsecure.HTTPError = HTTPError; window.textsecure.NotFoundError = NotFoundError; window.textsecure.WrongSwarmError = WrongSwarmError; + window.textsecure.WrongDifficultyError = WrongDifficultyError; })(); diff --git a/main.js b/main.js index 9cf854a7f..eaad6252c 100644 --- a/main.js +++ b/main.js @@ -156,6 +156,7 @@ function prepareURL(pathSegments, moreKeys) { cdnUrl: config.get('cdnUrl'), snodeServerPort: config.get('snodeServerPort'), localServerPort: config.get('localServerPort'), + defaultPoWDifficulty: config.get('defaultPoWDifficulty'), certificateAuthority: config.get('certificateAuthority'), environment: config.environment, node_version: process.versions.node, diff --git a/preload.js b/preload.js index 90c7f4885..5cad3915d 100644 --- a/preload.js +++ b/preload.js @@ -22,6 +22,7 @@ if (config.appInstance) { } window.platform = process.platform; +window.getDefaultPoWDifficulty = () => config.defaultPoWDifficulty; window.getTitle = () => title; window.getEnvironment = () => config.environment; window.getAppInstance = () => config.appInstance;