Skip to content
This repository was archived by the owner on Apr 6, 2020. It is now read-only.

Commit 6eed134

Browse files
authored
Merge pull request #47 from ethereumjs/multiple-hardfork-support
Validate blocks correctly for all known hardforks
2 parents 3159849 + 4451ac1 commit 6eed134

File tree

7 files changed

+90242
-9071
lines changed

7 files changed

+90242
-9071
lines changed

header.js

Lines changed: 34 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -105,33 +105,48 @@ var BlockHeader = module.exports = function (data, opts) {
105105
*/
106106
BlockHeader.prototype.canonicalDifficulty = function (parentBlock) {
107107
const hardfork = this._common.hardfork() || this._common.activeHardfork(utils.bufferToInt(this.number))
108-
109-
if (!this._common.hardforkGteHardfork(hardfork, 'byzantium')) {
110-
throw new Error('Difficulty validation only supported on blocks >= byzantium')
111-
}
112-
113108
const blockTs = new BN(this.timestamp)
114109
const parentTs = new BN(parentBlock.header.timestamp)
115110
const parentDif = new BN(parentBlock.header.difficulty)
116111
const minimumDifficulty = new BN(this._common.param('pow', 'minimumDifficulty', hardfork))
117112
var offset = parentDif.div(new BN(this._common.param('pow', 'difficultyBoundDivisor', hardfork)))
113+
var num = new BN(this.number)
114+
var a
115+
var cutoff
118116
var dif
119117

120-
// Byzantium
121-
// max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99)
122-
var uncleAddend = parentBlock.header.uncleHash.equals(utils.SHA3_RLP_ARRAY) ? 1 : 2
123-
var a = blockTs.sub(parentTs).idivn(9).ineg().iaddn(uncleAddend)
124-
var cutoff = new BN(-99)
125-
// MAX(cutoff, a)
126-
if (cutoff.cmp(a) === 1) {
127-
a = cutoff
128-
}
129-
dif = parentDif.add(offset.mul(a))
118+
if (this._common.hardforkGteHardfork(hardfork, 'byzantium')) {
119+
// max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99)
120+
var uncleAddend = parentBlock.header.uncleHash.equals(utils.SHA3_RLP_ARRAY) ? 1 : 2
121+
a = blockTs.sub(parentTs).idivn(9).ineg().iaddn(uncleAddend)
122+
cutoff = new BN(-99)
123+
// MAX(cutoff, a)
124+
if (cutoff.cmp(a) === 1) {
125+
a = cutoff
126+
}
127+
dif = parentDif.add(offset.mul(a))
130128

131-
// Byzantium difficulty bomb delay
132-
var num = new BN(this.number).isubn(3000000)
133-
if (num.ltn(0)) {
134-
num = new BN(0)
129+
// Byzantium difficulty bomb delay
130+
num.isubn(3000000)
131+
if (num.ltn(0)) {
132+
num = new BN(0)
133+
}
134+
} else if (this._common.hardforkGteHardfork(hardfork, 'homestead')) {
135+
// 1 - (block_timestamp - parent_timestamp) // 10
136+
a = blockTs.sub(parentTs).idivn(10).ineg().iaddn(1)
137+
cutoff = new BN(-99)
138+
// MAX(cutoff, a)
139+
if (cutoff.cmp(a) === 1) {
140+
a = cutoff
141+
}
142+
dif = parentDif.add(offset.mul(a))
143+
} else {
144+
// pre-homestead
145+
if (parentTs.addn(this._common.param('pow', 'durationLimit', hardfork)).cmp(blockTs) === 1) {
146+
dif = offset.add(parentDif)
147+
} else {
148+
dif = parentDif.sub(offset)
149+
}
135150
}
136151

137152
var exp = num.idivn(100000).isubn(2)

tests/difficulty.js

Lines changed: 36 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,6 @@ function normalize (data) {
1313
}
1414

1515
tape('[Header]: difficulty tests', t => {
16-
t.test('should test error cases', function (st) {
17-
var parentBlock = new Block(null, { 'chain': 'mainnet', 'hardfork': 'spuriousDragon' })
18-
var block = new Block(null, { 'chain': 'mainnet', 'hardfork': 'spuriousDragon' })
19-
20-
t.throws(function () { block.header.canonicalDifficulty(parentBlock) }, /Difficulty validation only supported on blocks >= byzantium$/, 'should throw on block with spuriousDragon HF initialization')
21-
st.end()
22-
})
23-
2416
function runDifficultyTests (test, parentBlock, block, msg) {
2517
normalize(test)
2618

@@ -29,42 +21,51 @@ tape('[Header]: difficulty tests', t => {
2921
t.assert(block.header.validateDifficulty(parentBlock), `test validateDifficulty (${msg})`)
3022
}
3123

32-
const testData = require('./testdata-difficulty.json')
33-
for (let testName in testData) {
34-
let test = testData[testName]
35-
let parentBlock = new Block(null, { 'chain': 'mainnet', 'hardfork': 'byzantium' })
36-
parentBlock.header.timestamp = test.parentTimestamp
37-
parentBlock.header.difficulty = test.parentDifficulty
38-
parentBlock.header.uncleHash = test.parentUncles
24+
const hardforkTestData = {
25+
'chainstart': require('./difficultyFrontier.json'),
26+
'homestead': require('./difficultyHomestead.json'),
27+
'byzantium': require('./difficultyByzantium.json')
28+
}
29+
for (let hardfork in hardforkTestData) {
30+
const testData = hardforkTestData[hardfork]
31+
for (let testName in testData) {
32+
let test = testData[testName]
33+
let parentBlock = new Block(null, { 'chain': 'mainnet', 'hardfork': hardfork })
34+
parentBlock.header.timestamp = test.parentTimestamp
35+
parentBlock.header.difficulty = test.parentDifficulty
36+
parentBlock.header.uncleHash = test.parentUncles
3937

40-
let block = new Block(null, { 'chain': 'mainnet', 'hardfork': 'byzantium' })
41-
block.header.timestamp = test.currentTimestamp
42-
block.header.difficulty = test.currentDifficulty
43-
block.header.number = test.currentBlockNumber
38+
let block = new Block(null, { 'chain': 'mainnet', 'hardfork': hardfork })
39+
block.header.timestamp = test.currentTimestamp
40+
block.header.difficulty = test.currentDifficulty
41+
block.header.number = test.currentBlockNumber
4442

45-
runDifficultyTests(test, parentBlock, block, 'fork determination by hardfork param')
43+
runDifficultyTests(test, parentBlock, block, 'fork determination by hardfork param')
44+
}
4645
}
4746

48-
for (let testName in testData) {
49-
let test = testData[testName]
50-
const BYZANTIUM_BLOCK = 4370000
51-
let parentBlock = new Block()
52-
parentBlock.header.timestamp = test.parentTimestamp
53-
parentBlock.header.difficulty = test.parentDifficulty
54-
parentBlock.header.uncleHash = test.parentUncles
55-
parentBlock.header.number = utils.intToBuffer(BYZANTIUM_BLOCK - 1)
47+
const chainTestData = {
48+
'mainnet': require('./difficultyMainNetwork.json'),
49+
'ropsten': require('./difficultyRopsten.json')
50+
}
51+
for (let chain in chainTestData) {
52+
const testData = chainTestData[chain]
53+
for (let testName in testData) {
54+
let test = testData[testName]
55+
let parentBlock = new Block(null, { 'chain': chain })
56+
parentBlock.header.timestamp = test.parentTimestamp
57+
parentBlock.header.difficulty = test.parentDifficulty
58+
parentBlock.header.uncleHash = test.parentUncles
5659

57-
let block = new Block()
58-
block.header.timestamp = test.currentTimestamp
59-
block.header.difficulty = test.currentDifficulty
60-
block.header.number = test.currentBlockNumber
60+
let block = new Block(null, { 'chain': chain })
61+
block.header.timestamp = test.currentTimestamp
62+
block.header.difficulty = test.currentDifficulty
63+
block.header.number = test.currentBlockNumber
6164

62-
if (utils.bufferToInt(block.header.number) >= BYZANTIUM_BLOCK) {
6365
runDifficultyTests(test, parentBlock, block, 'fork determination by block number')
64-
} else {
65-
t.throws(function () { block.header.canonicalDifficulty(parentBlock) }, /Difficulty validation only supported on blocks >= byzantium$/, 'should throw on block < byzantium')
6666
}
6767
}
68+
6869
t.end()
6970

7071
// Temporarily run local test selection

0 commit comments

Comments
 (0)