diff --git a/packages/block/CHANGELOG.md b/packages/block/CHANGELOG.md index 7bede32335..392cc6014c 100644 --- a/packages/block/CHANGELOG.md +++ b/packages/block/CHANGELOG.md @@ -155,7 +155,7 @@ as an input parameter. ### New Default Hardfork -**Breaking:** The default HF on the library has been updated from `petersburg` to `instanbul`, see PR [#906](https://github.com/ethereumjs/ethereumjs-vm/pull/906). +**Breaking:** The default HF on the library has been updated from `petersburg` to `istanbul`, see PR [#906](https://github.com/ethereumjs/ethereumjs-vm/pull/906). The HF setting is now automatically taken from the HF set for `Common.DEAULT_HARDFORK`, see PR [#863](https://github.com/ethereumjs/ethereumjs-vm/pull/863). @@ -179,6 +179,7 @@ in performance benefits for Node.js consumers, see [here](https://github.com/eth PR [#883](https://github.com/ethereumjs/ethereumjs-vm/pull/883) - Added `DAO` hardfork support (check for `extraData` attribute if `DAO` HF is active), PR [#843](https://github.com/ethereumjs/ethereumjs-vm/pull/843) +- Added the `calcDifficultyFromHeader` constructor option. If this `BlockHeader` is supplied, then the `difficulty` of the constructed `BlockHeader` will be set to the canonical difficulty (also if `difficulty` is set as parameter in the constructor). See [#929](https://github.com/ethereumjs/ethereumjs-vm/pull/929) **Changes and Refactoring** diff --git a/packages/block/src/header.ts b/packages/block/src/header.ts index bd2d7a42d7..19975707a5 100644 --- a/packages/block/src/header.ts +++ b/packages/block/src/header.ts @@ -223,6 +223,13 @@ export class BlockHeader { this._validateBufferLengths() this._checkDAOExtraData() + // Now we have set all the values of this Header, we possibly have set a dummy + // `difficulty` value (defaults to 0). If we have a `calcDifficultyFromHeader` + // block option parameter, we instead set difficulty to this value. + if (options.calcDifficultyFromHeader) { + this.difficulty = this.canonicalDifficulty(options.calcDifficultyFromHeader) + } + Object.freeze(this) } @@ -279,7 +286,7 @@ export class BlockHeader { let a = blockTs.sub(parentTs).idivn(9).ineg().iaddn(uncleAddend) const cutoff = new BN(-99) // MAX(cutoff, a) - if (cutoff.cmp(a) === 1) { + if (cutoff.gt(a)) { a = cutoff } dif = parentDif.add(offset.mul(a)) @@ -308,16 +315,14 @@ export class BlockHeader { let a = blockTs.sub(parentTs).idivn(10).ineg().iaddn(1) const cutoff = new BN(-99) // MAX(cutoff, a) - if (cutoff.cmp(a) === 1) { + if (cutoff.gt(a)) { a = cutoff } dif = parentDif.add(offset.mul(a)) } else { // pre-homestead if ( - parentTs - .addn(this._common.paramByHardfork('pow', 'durationLimit', hardfork)) - .cmp(blockTs) === 1 + parentTs.addn(this._common.paramByHardfork('pow', 'durationLimit', hardfork)).gt(blockTs) ) { dif = offset.add(parentDif) } else { @@ -330,7 +335,7 @@ export class BlockHeader { dif.iadd(new BN(2).pow(exp)) } - if (dif.cmp(minimumDifficulty) === -1) { + if (dif.lt(minimumDifficulty)) { dif = minimumDifficulty } @@ -411,7 +416,7 @@ export class BlockHeader { if (height) { const dif = height.sub(header.number) - if (!(dif.cmpn(8) === -1 && dif.cmpn(1) === 1)) { + if (!(dif.ltn(8) && dif.gtn(1))) { throw new Error('uncle block has a parent that is too old or too young') } } diff --git a/packages/block/src/types.ts b/packages/block/src/types.ts index 2627a9f690..23a5d678fd 100644 --- a/packages/block/src/types.ts +++ b/packages/block/src/types.ts @@ -2,6 +2,7 @@ import { AddressLike, BNLike, BufferLike } from 'ethereumjs-util' import Common from '@ethereumjs/common' import { TxData, JsonTx } from '@ethereumjs/tx' import { Block } from './block' +import { BlockHeader } from './header' /** * An object to set to which blockchain the blocks and their headers belong. This could be specified @@ -35,6 +36,13 @@ export interface BlockOptions { * Default: `false` */ initWithGenesisHeader?: boolean + + /** + * If a preceding `BlockHeader` (usually the parent header) is given the preceding + * header will be used to calculate the difficulty for this block and the calculated + * difficulty takes precedence over a provided static `difficulty` value. + */ + calcDifficultyFromHeader?: BlockHeader } /** diff --git a/packages/block/test/block.spec.ts b/packages/block/test/block.spec.ts index cccf1a394f..7811e7a563 100644 --- a/packages/block/test/block.spec.ts +++ b/packages/block/test/block.spec.ts @@ -157,4 +157,73 @@ tape('[Block]: block functions', function (t) { }, 'should not throw on DAO HF block with correct extra data') st.end() }) + + t.test( + 'should set canonical difficulty if I provide a calcDifficultyFromHeader header', + function (st) { + const common = new Common({ chain: 'mainnet', hardfork: 'chainstart' }) + const genesis = Block.genesis({}, { common }) + + const nextBlockHeaderData = { + number: genesis.header.number.addn(1), + timestamp: genesis.header.timestamp.addn(10), + } + + const blockWithoutDifficultyCalculation = Block.fromBlockData({ + header: nextBlockHeaderData, + }) + + // test if difficulty defaults to 0 + st.ok( + blockWithoutDifficultyCalculation.header.difficulty.eqn(0), + 'header difficulty should default to 0' + ) + + // test if we set difficulty if we have a "difficulty header" in options; also verify this is equal to reported canonical difficulty. + const blockWithDifficultyCalculation = Block.fromBlockData( + { + header: nextBlockHeaderData, + }, + { + calcDifficultyFromHeader: genesis.header, + } + ) + + st.ok( + blockWithDifficultyCalculation.header.difficulty.gtn(0), + 'header difficulty should be set if difficulty header is given' + ) + st.ok( + blockWithDifficultyCalculation.header + .canonicalDifficulty(genesis.header) + .eq(blockWithDifficultyCalculation.header.difficulty), + 'header difficulty is canonical difficulty if difficulty header is given' + ) + st.ok( + blockWithDifficultyCalculation.header.validateDifficulty(genesis.header), + 'difficulty should be valid if difficulty header is provided' + ) + + // test if we can provide a block which is too far ahead to still calculate difficulty + const noParentHeaderData = { + number: genesis.header.number.addn(1337), + timestamp: genesis.header.timestamp.addn(10), + } + + const block_farAhead = Block.fromBlockData( + { + header: noParentHeaderData, + }, + { + calcDifficultyFromHeader: genesis.header, + } + ) + + st.ok( + block_farAhead.header.difficulty.gtn(0), + 'should allow me to provide a bogus next block to calculate difficulty on when providing a difficulty header' + ) + st.end() + } + ) }) diff --git a/packages/blockchain/test/index.ts b/packages/blockchain/test/index.ts index 7156f46c13..545b6ce4ec 100644 --- a/packages/blockchain/test/index.ts +++ b/packages/blockchain/test/index.ts @@ -1,6 +1,6 @@ import { BN } from 'ethereumjs-util' import Common from '@ethereumjs/common' -import { Block, BlockHeader } from '@ethereumjs/block' +import { Block, BlockHeader, BlockOptions } from '@ethereumjs/block' import tape from 'tape' import Blockchain from '../src' import { generateBlockchain, generateBlocks, isConsecutive, createTestDB } from './util' @@ -81,12 +81,11 @@ tape('blockchain test', (t) => { header: { number, parentHash: lastBlock.hash(), - difficulty: lastBlock.canonicalDifficulty(lastBlock), timestamp: lastBlock.header.timestamp.addn(1), gasLimit, }, } - const block = Block.fromBlockData(blockData) + const block = Block.fromBlockData(blockData, { calcDifficultyFromHeader: lastBlock.header }) await blockchain.putBlock(block) blocks.push(block) @@ -118,12 +117,13 @@ tape('blockchain test', (t) => { header: { number: 1, parentHash: genesis.hash(), - difficulty: genesis.canonicalDifficulty(genesis), timestamp: genesis.header.timestamp.addn(1), gasLimit, }, } - const block = Block.fromBlockData(blockData) + const block = Block.fromBlockData(blockData, { + calcDifficultyFromHeader: genesis.header, + }) blocks.push(block) await blockchain.putBlock(block) @@ -377,11 +377,13 @@ tape('blockchain test', (t) => { const headerData = { number: 15, parentHash: blocks[14].hash(), - difficulty: blocks[14].canonicalDifficulty(blocks[14]), gasLimit: 8000000, timestamp: blocks[14].header.timestamp.addn(1), } - const forkHeader = BlockHeader.fromHeaderData(headerData, { common }) + const forkHeader = BlockHeader.fromHeaderData(headerData, { + common, + calcDifficultyFromHeader: blocks[14].header, + }) blockchain._heads['staletest'] = blockchain._headHeader @@ -400,11 +402,13 @@ tape('blockchain test', (t) => { const headerData = { number: 15, parentHash: blocks[14].hash(), - difficulty: blocks[14].canonicalDifficulty(blocks[14]), gasLimit: 8000000, timestamp: blocks[14].header.timestamp.addn(1), } - const forkHeader = BlockHeader.fromHeaderData(headerData, { common }) + const forkHeader = BlockHeader.fromHeaderData(headerData, { + common, + calcDifficultyFromHeader: blocks[14].header, + }) blockchain._heads['staletest'] = blockchain._headHeader @@ -558,11 +562,12 @@ tape('blockchain test', (t) => { const headerData = { number: 1, parentHash: genesis.hash(), - difficulty: genesis.canonicalDifficulty(genesis), gasLimit, timestamp: genesis.header.timestamp.addn(1), } - const header = BlockHeader.fromHeaderData(headerData) + const header = BlockHeader.fromHeaderData(headerData, { + calcDifficultyFromHeader: genesis.header, + }) await blockchain.putHeader(header) blockchain = new Blockchain({ @@ -586,7 +591,7 @@ tape('blockchain test', (t) => { }) const gasLimit = 8000000 const common = new Common({ chain: 'mainnet', hardfork: 'chainstart' }) - const opts = { common } + const opts: BlockOptions = { common } const genesis = Block.genesis({ header: { gasLimit } }, opts) await blockchain.putGenesis(genesis) @@ -595,30 +600,30 @@ tape('blockchain test', (t) => { header: { number: 1, parentHash: genesis.hash(), - difficulty: genesis.canonicalDifficulty(genesis), timestamp: genesis.header.timestamp.addn(3), gasLimit, }, } + opts.calcDifficultyFromHeader = genesis.header const block = Block.fromBlockData(blockData, opts) const headerData1 = { number: 1, parentHash: genesis.hash(), - difficulty: genesis.canonicalDifficulty(genesis), timestamp: genesis.header.timestamp.addn(1), gasLimit, } + opts.calcDifficultyFromHeader = genesis.header const header1 = BlockHeader.fromHeaderData(headerData1, opts) const headers = [header1] const headerData2 = { number: 2, parentHash: header1.hash(), - difficulty: header1.canonicalDifficulty(block.header), timestamp: header1.timestamp.addn(1), gasLimit, } + opts.calcDifficultyFromHeader = block.header const header2 = BlockHeader.fromHeaderData(headerData2, opts) headers.push(header2) @@ -655,7 +660,6 @@ tape('blockchain test', (t) => { header: { number: 1, parentHash: genesis.hash(), - difficulty: genesis.canonicalDifficulty(genesis), timestamp: genesis.header.timestamp.addn(1), gasLimit, }, @@ -668,9 +672,10 @@ tape('blockchain test', (t) => { const blocks = [ genesis, - Block.fromBlockData(blockData1, { common }), + Block.fromBlockData(blockData1, { common, calcDifficultyFromHeader: genesis.header }), Block.fromBlockData(blockData2, { common: new Common({ chain: 'ropsten', hardfork: 'chainstart' }), + calcDifficultyFromHeader: genesis.header, }), ] diff --git a/packages/blockchain/test/util.ts b/packages/blockchain/test/util.ts index cfa5f5e19f..80c10bca2d 100644 --- a/packages/blockchain/test/util.ts +++ b/packages/blockchain/test/util.ts @@ -22,12 +22,14 @@ export const generateBlocks = (numberOfBlocks: number, existingBlocks?: Block[]) header: { number: i, parentHash: lastBlock.hash(), - difficulty: lastBlock.canonicalDifficulty(lastBlock), gasLimit, timestamp: lastBlock.header.timestamp.addn(1), }, } - const block = Block.fromBlockData(blockData, opts) + const block = Block.fromBlockData(blockData, { + common, + calcDifficultyFromHeader: lastBlock.header, + }) blocks.push(block) } @@ -83,6 +85,7 @@ export const generateConsecutiveBlock = ( }, { common, + calcDifficultyFromHeader: parentBlock.header, } ) diff --git a/packages/tx/CHANGELOG.md b/packages/tx/CHANGELOG.md index 50d5866412..f6a99e0270 100644 --- a/packages/tx/CHANGELOG.md +++ b/packages/tx/CHANGELOG.md @@ -86,7 +86,7 @@ The `FakeTransaction` class was removed since its functionality can now be imple ### New Default Hardfork -**Breaking:** The default HF on the library has been updated from `petersburg` to `instanbul`, see PR [#906](https://github.com/ethereumjs/ethereumjs-vm/pull/906). +**Breaking:** The default HF on the library has been updated from `petersburg` to `istanbul`, see PR [#906](https://github.com/ethereumjs/ethereumjs-vm/pull/906). The HF setting is now automatically taken from the HF set for `Common.DEAULT_HARDFORK`, see PR [#863](https://github.com/ethereumjs/ethereumjs-vm/pull/863). diff --git a/packages/vm/CHANGELOG.md b/packages/vm/CHANGELOG.md index fc1029e0d8..0e948d3b7c 100644 --- a/packages/vm/CHANGELOG.md +++ b/packages/vm/CHANGELOG.md @@ -54,7 +54,7 @@ const common = new Common({ chain: 'mainnet', hardfork: 'spuriousDragon' }) const vm = new VM({ common }) ``` -**Breaking**: The default HF from the VM has been updated from `petersburg` to `instanbul`. +**Breaking**: The default HF from the VM has been updated from `petersburg` to `istanbul`. The HF setting is now automatically taken from the HF set for `Common.DEAULT_HARDFORK`, see PR [#906](https://github.com/ethereumjs/ethereumjs-vm/pull/906).