diff --git a/README.md b/README.md index 57e9aad..ee211ce 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,16 @@ c.genesis().hash // 0x41941023680923e0fe4d74a34bdac8141f2540e3ae90623718e47d66d1 c.bootstrapNodes() // Array with current nodes ``` +It is encouraged to also explicitly set the ``supportedHardforks`` if the initializing library +only supports a certain range of ``hardforks``: + +```javascript +let c = new Common('ropsten', null, ['byzantium', 'constantinople']) +``` + +This will e.g. throw an error when a param is requested for an unsupported hardfork and +like this prevents unpredicted behaviour. + # API See the API documentation for a full list of functions for accessing specific chain and diff --git a/docs/index.md b/docs/index.md index 09e87d7..ca50137 100644 --- a/docs/index.md +++ b/docs/index.md @@ -7,22 +7,23 @@ - [setHardfork][3] - [\_chooseHardfork][4] - [\_getHardfork][5] - - [param][6] - - [paramByBlock][7] - - [hardforkIsActiveOnBlock][8] - - [hardforkIsActiveOnChain][9] - - [activeHardforks][10] - - [hardforkBlock][11] - - [isHardforkBlock][12] - - [consensus][13] - - [finality][14] - - [genesis][15] - - [hardforks][16] - - [bootstrapNodes][17] - - [hardfork][18] - - [chainId][19] - - [chainName][20] - - [networkId][21] + - [\_isSupportedHardfork][6] + - [param][7] + - [paramByBlock][8] + - [hardforkIsActiveOnBlock][9] + - [hardforkIsActiveOnChain][10] + - [activeHardforks][11] + - [hardforkBlock][12] + - [isHardforkBlock][13] + - [consensus][14] + - [finality][15] + - [genesis][16] + - [hardforks][17] + - [bootstrapNodes][18] + - [hardfork][19] + - [chainId][20] + - [chainName][21] + - [networkId][22] ## Common @@ -30,8 +31,9 @@ Common class to access chain and hardfork parameters **Parameters** -- `chain` **([String][22] \| [Number][23])** String ('mainnet') or Number (1) chain representation -- `hardfork` **[String][22]** String identifier ('byzantium') for hardfork (optional) +- `chain` **([String][23] \| [Number][24])** String ('mainnet') or Number (1) chain representation +- `hardfork` **[String][23]** String identifier ('byzantium') for hardfork (optional) +- `supportedHardforks` **[Array][25]** Limit parameter returns to the given hardforks (optional) ### setChain @@ -39,7 +41,7 @@ Sets the chain **Parameters** -- `chain` **([String][22] \| [Number][23])** String ('mainnet') or Number (1) chain representation +- `chain` **([String][23] \| [Number][24])** String ('mainnet') or Number (1) chain representation ### setHardfork @@ -47,7 +49,7 @@ Sets the hardfork to get params for **Parameters** -- `hardfork` **[String][22]** String identifier ('byzantium') +- `hardfork` **[String][23]** String identifier ('byzantium') ### \_chooseHardfork @@ -55,9 +57,9 @@ Internal helper function to choose between hardfork set and hardfork provided as **Parameters** -- `hardfork` **[String][22]** Hardfork given to function as a parameter +- `hardfork` **[String][23]** Hardfork given to function as a parameter -Returns **[String][22]** Hardfork chosen to be used +Returns **[String][23]** Hardfork chosen to be used ### \_getHardfork @@ -65,19 +67,29 @@ Internal helper function, returns the params for the given hardfork for the chai **Parameters** -- `hardfork` **[String][22]** Hardfork name +- `hardfork` **[String][23]** Hardfork name Returns **Dictionary** +### \_isSupportedHardfork + +Internal helper function to check if a hardfork is set to be supported by the library + +**Parameters** + +- `hardfork` **[String][23]** Hardfork name + +Returns **[Boolean][26]** True if hardfork is supported + ### param Returns the parameter corresponding to a hardfork **Parameters** -- `topic` **[String][22]** Parameter topic ('gasConfig', 'gasPrices', 'vm', 'pow', 'casper', 'sharding') -- `name` **[String][22]** Parameter name (e.g. 'minGasLimit' for 'gasConfig' topic) -- `hardfork` **[String][22]** Hardfork name, optional if hardfork set +- `topic` **[String][23]** Parameter topic ('gasConfig', 'gasPrices', 'vm', 'pow', 'casper', 'sharding') +- `name` **[String][23]** Parameter name (e.g. 'minGasLimit' for 'gasConfig' topic) +- `hardfork` **[String][23]** Hardfork name, optional if hardfork set ### paramByBlock @@ -85,9 +97,9 @@ Returns a parameter for the hardfork active on block number **Parameters** -- `topic` **[String][22]** Parameter topic -- `name` **[String][22]** Parameter name -- `blockNumber` **[Number][23]** Block number +- `topic` **[String][23]** Parameter topic +- `name` **[String][23]** Parameter name +- `blockNumber` **[Number][24]** Block number ### hardforkIsActiveOnBlock @@ -95,10 +107,12 @@ Checks if a hardfork is active for a given block number **Parameters** -- `hardfork` **[String][22]** Hardfork name -- `blockNumber` **[Number][23]** +- `hardfork` **[String][23]** Hardfork name +- `blockNumber` **[Number][24]** +- `opts` **[Array][25]** + - `opts.onlySupported` **Array.Boolean** optional, only allow supported HFs (default: false) -Returns **[Boolean][24]** +Returns **[Boolean][26]** ### hardforkIsActiveOnChain @@ -106,9 +120,11 @@ Checks if the hardfork provided is active on the chain **Parameters** -- `hardfork` **[String][22]** +- `hardfork` **[String][23]** +- `opts` **[Array][25]** + - `opts.onlySupported` **Array.Boolean** optional, only allow supported HFs (default: false) -Returns **[Boolean][24]** +Returns **[Boolean][26]** ### activeHardforks @@ -116,7 +132,9 @@ Returns the active hardfork switches for the current chain **Parameters** -- `blockNumber` **[Number][23]** up to block if provided, otherwise for the whole chain +- `blockNumber` **[Number][24]** up to block if provided, otherwise for the whole chain +- `opts` **[Array][25]** + - `opts.onlySupported` **Array.Boolean** optional, limit results to supported HFs (default: false) Returns **[Array][25]** Array with hardfork arrays @@ -126,9 +144,9 @@ Returns the hardfork change block for the given hardfork **Parameters** -- `hardfork` **[String][22]** Hardfork name +- `hardfork` **[String][23]** Hardfork name -Returns **[Number][23]** Block number +Returns **[Number][24]** Block number ### isHardforkBlock @@ -136,10 +154,10 @@ True if block number provided is the hardfork change block of the current chain **Parameters** -- `hardfork` **[String][22]** Hardfork name -- `blockNumber` **[Number][23]** Number of the block to check +- `hardfork` **[String][23]** Hardfork name +- `blockNumber` **[Number][24]** Number of the block to check -Returns **[Boolean][24]** +Returns **[Boolean][26]** ### consensus @@ -147,9 +165,9 @@ Provide the consensus type for the hardfork set or provided as param **Parameters** -- `hardfork` **[String][22]** Hardfork name, optional if hardfork set +- `hardfork` **[String][23]** Hardfork name, optional if hardfork set -Returns **[String][22]** Consensus type (e.g. 'pow', 'poa') +Returns **[String][23]** Consensus type (e.g. 'pow', 'poa') ### finality @@ -157,9 +175,9 @@ Provide the finality type for the hardfork set or provided as param **Parameters** -- `hardfork` **[String][22]** Hardfork name, optional if hardfork set +- `hardfork` **[String][23]** Hardfork name, optional if hardfork set -Returns **[String][22]** Finality type (e.g. 'pos', null of no finality) +Returns **[String][23]** Finality type (e.g. 'pos', null of no finality) ### genesis @@ -183,25 +201,25 @@ Returns **Dictionary** Dict with bootstrap nodes Returns the hardfork set -Returns **[String][22]** Hardfork name +Returns **[String][23]** Hardfork name ### chainId Returns the Id of current chain -Returns **[Number][23]** chain Id +Returns **[Number][24]** chain Id ### chainName Returns the name of current chain -Returns **[String][22]** chain name (lower case) +Returns **[String][23]** chain name (lower case) ### networkId Returns the Id of current network -Returns **[Number][23]** network Id +Returns **[Number][24]** network Id [1]: #common @@ -213,42 +231,44 @@ Returns **[Number][23]** network Id [5]: #_gethardfork -[6]: #param +[6]: #_issupportedhardfork -[7]: #parambyblock +[7]: #param -[8]: #hardforkisactiveonblock +[8]: #parambyblock -[9]: #hardforkisactiveonchain +[9]: #hardforkisactiveonblock -[10]: #activehardforks +[10]: #hardforkisactiveonchain -[11]: #hardforkblock +[11]: #activehardforks -[12]: #ishardforkblock +[12]: #hardforkblock -[13]: #consensus +[13]: #ishardforkblock -[14]: #finality +[14]: #consensus -[15]: #genesis +[15]: #finality -[16]: #hardforks +[16]: #genesis -[17]: #bootstrapnodes +[17]: #hardforks -[18]: #hardfork +[18]: #bootstrapnodes -[19]: #chainid +[19]: #hardfork -[20]: #chainname +[20]: #chainid -[21]: #networkid +[21]: #chainname -[22]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String +[22]: #networkid -[23]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number +[23]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/String -[24]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean +[24]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Number [25]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Array + +[26]: https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Boolean diff --git a/index.js b/index.js index 785c978..4ae9b79 100644 --- a/index.js +++ b/index.js @@ -9,11 +9,13 @@ class Common { * @constructor * @param {String|Number} chain String ('mainnet') or Number (1) chain representation * @param {String} hardfork String identifier ('byzantium') for hardfork (optional) + * @param {Array} supportedHardforks Limit parameter returns to the given hardforks (optional) */ - constructor (chain, hardfork) { + constructor (chain, hardfork, supportedHardforks) { this.setChain(chain) this._hardfork = null - if (hardfork !== undefined) { + this._supportedHardforks = supportedHardforks === undefined ? [] : supportedHardforks + if (hardfork) { this.setHardfork(hardfork) } } @@ -45,6 +47,9 @@ class Common { * @param {String} hardfork String identifier ('byzantium') */ setHardfork (hardfork) { + if (!this._isSupportedHardfork(hardfork)) { + throw new Error(`Hardfork ${hardfork} not set as supported in supportedHardforks`) + } let changed = false for (let hfChanges of hardforkChanges) { if (hfChanges[0] === hardfork) { @@ -69,6 +74,8 @@ class Common { } else { hardfork = this._hardfork } + } else if (!this._isSupportedHardfork(hardfork)) { + throw new Error(`Hardfork ${hardfork} not set as supported in supportedHardforks`) } return hardfork } @@ -86,6 +93,22 @@ class Common { throw new Error(`Hardfork ${hardfork} not defined for chain ${this.chainName()}`) } + /** + * Internal helper function to check if a hardfork is set to be supported by the library + * @param {String} hardfork Hardfork name + * @returns {Boolean} True if hardfork is supported + */ + _isSupportedHardfork (hardfork) { + if (this._supportedHardforks.length > 0) { + for (let supportedHf of this._supportedHardforks) { + if (hardfork === supportedHf) return true + } + } else { + return true + } + return false + } + /** * Returns the parameter corresponding to a hardfork * @param {String} topic Parameter topic ('gasConfig', 'gasPrices', 'vm', 'pow', 'casper', 'sharding') @@ -127,9 +150,15 @@ class Common { * Checks if a hardfork is active for a given block number * @param {String} hardfork Hardfork name * @param {Number} blockNumber + * @param {Array} opts + * @param {Array.Boolean} opts.onlySupported optional, only allow supported HFs (default: false) * @returns {Boolean} */ - hardforkIsActiveOnBlock (hardfork, blockNumber) { + hardforkIsActiveOnBlock (hardfork, blockNumber, opts) { + opts = opts !== undefined ? opts : [] + if (opts.onlySupported && !this._isSupportedHardfork(hardfork)) { + throw new Error(`Not allowed to be called with an unsupported hardfork`) + } let hfBlock = this.hardforkBlock(hardfork) if (hfBlock !== null && blockNumber >= hfBlock) return true return false @@ -138,9 +167,15 @@ class Common { /** * Checks if the hardfork provided is active on the chain * @param {String} hardfork + * @param {Array} opts + * @param {Array.Boolean} opts.onlySupported optional, only allow supported HFs (default: false) * @returns {Boolean} */ - hardforkIsActiveOnChain (hardfork) { + hardforkIsActiveOnChain (hardfork, opts) { + opts = opts !== undefined ? opts : [] + if (opts.onlySupported && !this._isSupportedHardfork(hardfork)) { + throw new Error(`Not allowed to be called with an unsupported hardfork`) + } for (let hf of this.hardforks()) { if (hf['name'] === hardfork && hf['block'] !== null) return true } @@ -150,14 +185,18 @@ class Common { /** * Returns the active hardfork switches for the current chain * @param {Number} blockNumber up to block if provided, otherwise for the whole chain + * @param {Array} opts + * @param {Array.Boolean} opts.onlySupported optional, limit results to supported HFs (default: false) * @return {Array} Array with hardfork arrays */ - activeHardforks (blockNumber) { + activeHardforks (blockNumber, opts) { + opts = opts !== undefined ? opts : [] let activeHardforks = [] let hfs = this.hardforks() for (let hf of hfs) { if (hf['block'] === null) continue - if (blockNumber !== undefined && blockNumber < hf['block']) break + if ((blockNumber !== undefined && blockNumber !== null) && blockNumber < hf['block']) break + if (opts.onlySupported && !this._isSupportedHardfork(hf['name'])) continue activeHardforks.push(hf) } diff --git a/tests/chains.js b/tests/chains.js index 664cf14..d35ffc6 100644 --- a/tests/chains.js +++ b/tests/chains.js @@ -8,6 +8,7 @@ tape('[Common]: Initialization / Chain params', function (t) { st.equal(c.chainId(), 1, 'should return correct chain Id') st.equal(c.networkId(), 1, 'should return correct network Id') st.equal(c.hardfork(), null, 'should set hardfork to null') + st.equal(c._isSupportedHardfork('constantinople'), true, 'should not restrict supported HFs') c = new Common(1) st.equal(c.chainName(), 'mainnet', 'should initialize with chain Id') @@ -22,9 +23,18 @@ tape('[Common]: Initialization / Chain params', function (t) { st.end() }) + t.test('Should initialize with supportedHardforks provided', function (st) { + let c = new Common('mainnet', 'byzantium', ['byzantium', 'constantinople']) + st.equal(c._isSupportedHardfork('byzantium'), true, 'should return true for supported HF') + st.equal(c._isSupportedHardfork('spuriousDragon'), false, 'should return false for unsupported HF') + + st.end() + }) + t.test('Should handle initialization errors', function (st) { st.throws(function () { new Common('chainnotexisting') }, /not supported$/, 'should throw an exception on non-existing chain') // eslint-disable-line no-new st.throws(function () { new Common('mainnet', 'hardforknotexisting') }, /not supported$/, 'should throw an exception on non-existing hardfork') // eslint-disable-line no-new + st.throws(function () { new Common('mainnet', 'spuriousDragon', ['byzantium', 'constantinople']) }, /supportedHardforks$/, 'should throw an exception on conflicting active/supported HF params') // eslint-disable-line no-new st.end() }) diff --git a/tests/hardforks.js b/tests/hardforks.js index bce3df8..f24e521 100644 --- a/tests/hardforks.js +++ b/tests/hardforks.js @@ -45,6 +45,9 @@ tape('[Common]: Hardfork logic', function (t) { st.equal(c.activeHardforks(9).length, 3, 'should return 3 active hardforks for Ropsten up to block 9') st.equal(c.activeHardforks(10).length, 4, 'should return 4 active hardforks for Ropsten up to block 10') + c = new Common('ropsten', null, ['spuriousDragon', 'byzantium', 'constantinople']) + st.equal(c.activeHardforks(null, { onlySupported: true }).length, 2, 'should return 2 active HFs when restricted to supported HFs') + st.end() }) @@ -54,6 +57,10 @@ tape('[Common]: Hardfork logic', function (t) { st.equal(c.hardforkIsActiveOnChain('dao'), false, 'should return false for dao on Ropsten') st.equal(c.hardforkIsActiveOnChain('hybridCasper'), false, 'should return false for hybridCasper on Ropsten') st.equal(c.hardforkIsActiveOnChain('notexistinghardfork'), false, 'should return false for a non-existing HF on Ropsten') + st.doesNotThrow(function () { c.hardforkIsActiveOnChain('spuriousDragon', { onlySupported: true }) }, /unsupported hardfork$/, 'should not throw with unsupported Hf and onlySupported set to false') // eslint-disable-line no-new + + c = new Common('ropsten', null, ['byzantium', 'constantinople']) + st.throws(function () { c.hardforkIsActiveOnChain('spuriousDragon', { onlySupported: true }) }, /unsupported hardfork$/, 'should throw with unsupported Hf and onlySupported set to true') // eslint-disable-line no-new st.end() }) diff --git a/tests/params.js b/tests/params.js index 6655b12..1587776 100644 --- a/tests/params.js +++ b/tests/params.js @@ -21,6 +21,10 @@ tape('[Common]: Parameter access', function (t) { c.setHardfork('byzantium') st.equal(c.param('gasPrices', 'ecAddGas'), 500, 'Should return correct value for HF set in class') + c = new Common('mainnet', 'byzantium', ['byzantium', 'constantinople']) + st.throws(function () { c.param('gasPrices', 'expByte', 'spuriousDragon') }, /supportedHardforks$/, 'Should throw when calling param() with an unsupported hardfork') + st.throws(function () { c.paramByBlock('gasPrices', 'expByte', 0) }, /supportedHardforks$/, 'Should throw when calling paramByBlock() with an unsupported hardfork') + st.end() })