From 5ec0eacc2ef653fe14f6395e7e1a2f2a5ec85c01 Mon Sep 17 00:00:00 2001 From: "Michael Bradley, Jr" Date: Tue, 16 Jul 2019 20:51:57 -0500 Subject: [PATCH] implement fallback from built-in scrypt to scryptsy/scrypt packages This commit introduces an additional fallback behavior and console warning in the case that Node's built-in scrypt experiences memory exhaustion, as was discovered in the 1.x test suite with the static tests (see #2938). A large `n` parameter is the culprit, but that's not the case when using the 3rd party (deprecated) scrypt package. The `scryptsy` package can handle a large `n`, but performance does suffer considerably. --- .../web3-eth-accounts/src/crypto/Scrypt.js | 56 ++++++++++++++++--- 1 file changed, 47 insertions(+), 9 deletions(-) diff --git a/packages/web3-eth-accounts/src/crypto/Scrypt.js b/packages/web3-eth-accounts/src/crypto/Scrypt.js index 6ee02dc5186..b49997d3f33 100644 --- a/packages/web3-eth-accounts/src/crypto/Scrypt.js +++ b/packages/web3-eth-accounts/src/crypto/Scrypt.js @@ -5,26 +5,64 @@ let scrypt; const isNode = Object.prototype.toString.call(typeof process !== 'undefined' ? process : 0) === '[object process]'; if (isNode) { const NODE_MIN_VER_WITH_BUILTIN_SCRYPT = '10.5.0'; + const NODE_MIN_VER_INCOMPAT_SCRYPT_PKG = '12.0.0'; const semver = require('semver'); const useNodeBuiltin = isNode && semver.Range('>=' + NODE_MIN_VER_WITH_BUILTIN_SCRYPT).test(process.version); + const tryScryptPackage = (function() { + let scryptPackage; + return function() { + if (scryptPackage !== undefined) { + return scryptPackage; + } + try { + scryptPackage = require('scrypt'); + } catch (error) { + if (/was compiled against a different/.test(error.message)) { + throw error; + } + scryptPackage = null; + } + return scryptPackage; + }; + })(); + + const canImprove = function(nodeVer) { + return `can improve web3's peformance when running Node.js versions older than ${nodeVer} by installing the (deprecated) scrypt package in your project`; + }; + if (useNodeBuiltin) { const crypto = require('crypto'); + let fallbackCount = 0; scrypt = function(key, salt, N, r, p, dkLength) { - return crypto.scryptSync(key, salt, dkLength, {N, r, p}); + try { + return crypto.scryptSync(key, salt, dkLength, {N, r, p}); + } catch (error) { + if (/scrypt:memory limit exceeded/.test(error.message)) { + const scryptPackage = tryScryptPackage(); + if (scryptPackage) { + return scryptPackage.hashSync(key, {N: N, r: r, p: p}, dkLength, salt); + } + fallbackCount += 1; + console.warn( + '\u001B[33m%s\u001B[0m', + `Memory limit exceeded for Node's built-in crypto.scrypt, falling back to scryptsy (times: ${fallbackCount}), if this happens frequently you ${canImprove( + NODE_MIN_VER_INCOMPAT_SCRYPT_PKG + )}` + ); + return scryptsy(key, salt, N, r, p, dkLength); + } + throw error; + } }; } else { - let scryptPackage; - try { - scryptPackage = require('scrypt'); + const scryptPackage = tryScryptPackage(); + if (scryptPackage) { scrypt = function(key, salt, N, r, p, dkLength) { return scryptPackage.hashSync(key, {N, r, p}, dkLength, salt); }; - } catch (error) { - console.warn( - '\u001B[33m%s\u001B[0m', - `You can improve web3's peformance when running Node.js versions older than ${NODE_MIN_VER_WITH_BUILTIN_SCRYPT} by installing the (deprecated) scrypt package in your project` - ); + } else { + console.warn('\u001B[33m%s\u001B[0m', `You ${canImprove(NODE_MIN_VER_WITH_BUILTIN_SCRYPT)}`); } } }