From f0e547d41084455c4bad2e1ae4a5f76bbac2f870 Mon Sep 17 00:00:00 2001 From: holgerd77 Date: Thu, 2 Jul 2020 16:26:19 +0200 Subject: [PATCH] common: added new methods nextHardforkBlock, isNextHardforkBlock and hardforkForForkHash and associated tests --- packages/common/src/index.ts | 49 ++++++++++++++++++++++--- packages/common/tests/hardforks.ts | 59 ++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 6 deletions(-) diff --git a/packages/common/src/index.ts b/packages/common/src/index.ts index 7fb9eb381d..2c4d740124 100644 --- a/packages/common/src/index.ts +++ b/packages/common/src/index.ts @@ -345,18 +345,43 @@ export default class Common { } /** - * True if block number provided is the hardfork (given or set) change block of the current chain + * True if block number provided is the hardfork (given or set) change block * @param blockNumber Number of the block to check * @param hardfork Hardfork name, optional if HF set * @returns True if blockNumber is HF block */ isHardforkBlock(blockNumber: number, hardfork?: string): boolean { hardfork = this._chooseHardfork(hardfork, false) - if (this.hardforkBlock(hardfork) === blockNumber) { - return true - } else { - return false - } + return this.hardforkBlock(hardfork) === blockNumber + } + + /** + * Returns the change block for the next hardfork after the hardfork provided or set + * @param hardfork Hardfork name, optional if HF set + * @returns Block number or null if not available + */ + nextHardforkBlock(hardfork?: string): number | null { + hardfork = this._chooseHardfork(hardfork, false) + const hfBlock = this.hardforkBlock(hardfork) + // Next fork block number or null if none available + // Logic: if accumulator is still null and on the first occurence of + // a block greater than the current hfBlock set the accumulator, + // pass on the accumulator as the final result from this time on + const nextHfBlock = this.hardforks().reduce((acc: number, hf: any) => { + return hf.block > hfBlock && acc === null ? hf.block : acc + }, null) + return nextHfBlock + } + + /** + * True if block number provided is the hardfork change block following the hardfork given or set + * @param blockNumber Number of the block to check + * @param hardfork Hardfork name, optional if HF set + * @returns True if blockNumber is HF block + */ + isNextHardforkBlock(blockNumber: number, hardfork?: string): boolean { + hardfork = this._chooseHardfork(hardfork, false) + return this.nextHardforkBlock(hardfork) === blockNumber } /** @@ -407,6 +432,18 @@ export default class Common { return this._calcForkHash(hardfork) } + /** + * + * @param forkHash Fork hash as a hex string + * @returns Array with hardfork data (name, block, forkHash) + */ + hardforkForForkHash(forkHash: string): any | null { + const resArray = this.hardforks().filter((hf: any) => { + return hf.forkHash === forkHash + }) + return resArray.length === 1 ? resArray[0] : null + } + /** * Returns the Genesis parameters of current chain * @returns Genesis dictionary diff --git a/packages/common/tests/hardforks.ts b/packages/common/tests/hardforks.ts index 9b44842306..97e25d4d83 100644 --- a/packages/common/tests/hardforks.ts +++ b/packages/common/tests/hardforks.ts @@ -59,6 +59,52 @@ tape('[Common]: Hardfork logic', function (t: tape.Test) { st.end() }) + t.test('nextHardforkBlock()', function (st: tape.Test) { + let c = new Common('rinkeby', 'chainstart') + let msg = + 'should work with HF set / return correct next HF block for chainstart (rinkeby: chainstart -> homestead)' + st.equal(c.nextHardforkBlock(), 1, msg) + + msg = + 'should correctly skip a HF where block is set to null (rinkeby: homestead -> (dao) -> tangerineWhistle)' + st.equal(c.nextHardforkBlock('homestead'), 2, msg) + + msg = 'should return correct next HF (rinkeby: byzantium -> constantinople)' + st.equal(c.nextHardforkBlock('byzantium'), 3660663, msg) + + msg = 'should return null if next HF is not available (rinkeby: istanbul -> berlin)' + st.equal(c.nextHardforkBlock('istanbul'), null, msg) + + msg = + 'should work correctly along the need to skip several forks (ropsten: chainstart -> (homestead) -> (dao) -> (tangerineWhistle) -> spuriousDragon)' + c = new Common('ropsten', 'chainstart') + st.equal(c.nextHardforkBlock(), 10, msg) + + st.end() + }) + + t.test('isNextHardforkBlock()', function (st: tape.Test) { + let c = new Common('rinkeby', 'chainstart') + let msg = + 'should work with HF set / return true fro correct next HF block for chainstart (rinkeby: chainstart -> homestead)' + st.equal(c.isNextHardforkBlock(1), true, msg) + + msg = + 'should correctly skip a HF where block is set to null (rinkeby: homestead -> (dao) -> tangerineWhistle)' + st.equal(c.isNextHardforkBlock(2, 'homestead'), true, msg) + + msg = 'should return true for correct next HF (rinkeby: byzantium -> constantinople)' + st.equal(c.isNextHardforkBlock(3660663, 'byzantium'), true, msg) + + msg = 'should return false for a block number too low (rinkeby: byzantium -> constantinople)' + st.equal(c.isNextHardforkBlock(124, 'byzantium'), false, msg) + + msg = 'should return false for a block number too hight (rinkeby: byzantium -> constantinople)' + st.equal(c.isNextHardforkBlock(605948938, 'byzantium'), false, msg) + + st.end() + }) + t.test('activeHardforks()', function (st: tape.Test) { let c = new Common('ropsten') let msg = 'should return 9 active hardforks for Ropsten' @@ -274,4 +320,17 @@ tape('[Common]: Hardfork logic', function (t: tape.Test) { st.end() }) + + t.test('hardforkForForkHash()', function (st: tape.Test) { + let c = new Common('mainnet') + + let msg = 'should return the correct HF array for a matching forkHash' + const res = c.hardforkForForkHash('0x3edd5b10') + st.equal(res.name, 'spuriousDragon', msg) + + msg = 'should return null for a forkHash not matching any HF' + st.equal(c.hardforkForForkHash('0x12345'), null, msg) + + st.end() + }) })