UNPKG

9.31 kBJavaScriptView Raw
1const Common = require('ethereumjs-common').default
2const utils = require('ethereumjs-util')
3const BN = utils.BN
4 /**
5 * An object that repersents the block header
6 * @constructor
7 * @param {Array} data raw data, deserialized
8 * @param {Array} opts Options
9 * @param {String|Number} opts.chain The chain for the block header [default: 'mainnet']
10 * @param {String} opts.hardfork Hardfork for the block header [default: null, block number-based behaviour]
11 * @param {Object} opts.common Alternatively pass a Common instance instead of setting chain/hardfork directly
12 * @prop {Buffer} parentHash the blocks' parent's hash
13 * @prop {Buffer} uncleHash sha3(rlp_encode(uncle_list))
14 * @prop {Buffer} coinbase the miner address
15 * @prop {Buffer} stateRoot The root of a Merkle Patricia tree
16 * @prop {Buffer} transactionTrie the root of a Trie containing the transactions
17 * @prop {Buffer} receiptTrie the root of a Trie containing the transaction Reciept
18 * @prop {Buffer} bloom
19 * @prop {Buffer} difficulty
20 * @prop {Buffer} number the block's height
21 * @prop {Buffer} gasLimit
22 * @prop {Buffer} gasUsed
23 * @prop {Buffer} timestamp
24 * @prop {Buffer} extraData
25 * @prop {Array.<Buffer>} raw an array of buffers containing the raw blocks.
26 */
27var BlockHeader = module.exports = function (data, opts) {
28 opts = opts || {}
29
30 if (opts.common) {
31 if (opts.chain) {
32 throw new Error('Instantiation with both opts.common and opts.chain parameter not allowed!')
33 }
34 this._common = opts.common
35 } else {
36 let chain = opts.chain ? opts.chain : 'mainnet'
37 let hardfork = opts.hardfork ? opts.hardfork : null
38 this._common = new Common(chain, hardfork)
39 }
40
41 var fields = [{
42 name: 'parentHash',
43 length: 32,
44 default: utils.zeros(32)
45 }, {
46 name: 'uncleHash',
47 default: utils.SHA3_RLP_ARRAY
48 }, {
49 name: 'coinbase',
50 length: 20,
51 default: utils.zeros(20)
52 }, {
53 name: 'stateRoot',
54 length: 32,
55 default: utils.zeros(32)
56 }, {
57 name: 'transactionsTrie',
58 length: 32,
59 default: utils.SHA3_RLP
60 }, {
61 name: 'receiptTrie',
62 length: 32,
63 default: utils.SHA3_RLP
64 }, {
65 name: 'bloom',
66 default: utils.zeros(256)
67 }, {
68 name: 'difficulty',
69 default: Buffer.from([])
70 }, {
71 name: 'number',
72 // TODO: params.homeSteadForkNumber.v left for legacy reasons, replace on future release
73 default: utils.intToBuffer(1150000)
74 }, {
75 name: 'gasLimit',
76 default: Buffer.from('ffffffffffffff', 'hex')
77 }, {
78 name: 'gasUsed',
79 empty: true,
80 default: Buffer.from([])
81 }, {
82 name: 'timestamp',
83 default: Buffer.from([])
84 }, {
85 name: 'extraData',
86 allowZero: true,
87 empty: true,
88 default: Buffer.from([])
89 }, {
90 name: 'mixHash',
91 default: utils.zeros(32)
92 // length: 32
93 }, {
94 name: 'nonce',
95 default: utils.zeros(8) // sha3(42)
96 }]
97 utils.defineProperties(this, fields, data)
98}
99
100/**
101 * Returns the canoncical difficulty of the block
102 * @method canonicalDifficulty
103 * @param {Block} parentBlock the parent `Block` of the this header
104 * @return {BN}
105 */
106BlockHeader.prototype.canonicalDifficulty = function (parentBlock) {
107 const hardfork = this._common.hardfork() || this._common.activeHardfork(utils.bufferToInt(this.number))
108 const blockTs = new BN(this.timestamp)
109 const parentTs = new BN(parentBlock.header.timestamp)
110 const parentDif = new BN(parentBlock.header.difficulty)
111 const minimumDifficulty = new BN(this._common.param('pow', 'minimumDifficulty', hardfork))
112 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
116 var dif
117
118 if (this._common.hardforkGteHardfork(hardfork, 'byzantium')) {
119 // max((2 if len(parent.uncles) else 1) - ((timestamp - parent.timestamp) // 9), -99) (EIP100)
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))
128 }
129
130 if (this._common.hardforkGteHardfork(hardfork, 'muirGlacier')) {
131 // Istanbul/Berlin difficulty bomb delay (EIP2384)
132 num.isubn(9000000)
133 if (num.ltn(0)) {
134 num = new BN(0)
135 }
136 } else if (this._common.hardforkGteHardfork(hardfork, 'constantinople')) {
137 // Constantinople difficulty bomb delay (EIP1234)
138 num.isubn(5000000)
139 if (num.ltn(0)) {
140 num = new BN(0)
141 }
142 } else if (this._common.hardforkGteHardfork(hardfork, 'byzantium')) {
143 // Byzantium difficulty bomb delay (EIP649)
144 num.isubn(3000000)
145 if (num.ltn(0)) {
146 num = new BN(0)
147 }
148 } else if (this._common.hardforkGteHardfork(hardfork, 'homestead')) {
149 // 1 - (block_timestamp - parent_timestamp) // 10
150 a = blockTs.sub(parentTs).idivn(10).ineg().iaddn(1)
151 cutoff = new BN(-99)
152 // MAX(cutoff, a)
153 if (cutoff.cmp(a) === 1) {
154 a = cutoff
155 }
156 dif = parentDif.add(offset.mul(a))
157 } else {
158 // pre-homestead
159 if (parentTs.addn(this._common.param('pow', 'durationLimit', hardfork)).cmp(blockTs) === 1) {
160 dif = offset.add(parentDif)
161 } else {
162 dif = parentDif.sub(offset)
163 }
164 }
165
166 var exp = num.idivn(100000).isubn(2)
167 if (!exp.isNeg()) {
168 dif.iadd(new BN(2).pow(exp))
169 }
170
171 if (dif.cmp(minimumDifficulty) === -1) {
172 dif = minimumDifficulty
173 }
174
175 return dif
176}
177
178/**
179 * checks that the block's `difficuly` matches the canonical difficulty
180 * @method validateDifficulty
181 * @param {Block} parentBlock this block's parent
182 * @return {Boolean}
183 */
184BlockHeader.prototype.validateDifficulty = function (parentBlock) {
185 const dif = this.canonicalDifficulty(parentBlock)
186 return dif.cmp(new BN(this.difficulty)) === 0
187}
188
189/**
190 * Validates the gasLimit
191 * @method validateGasLimit
192 * @param {Block} parentBlock this block's parent
193 * @returns {Boolean}
194 */
195BlockHeader.prototype.validateGasLimit = function (parentBlock) {
196 const pGasLimit = new BN(parentBlock.header.gasLimit)
197 const gasLimit = new BN(this.gasLimit)
198 const hardfork = this._common.hardfork() ? this._common.hardfork() : this._common.activeHardfork(this.number)
199 const a = pGasLimit.div(new BN(this._common.param('gasConfig', 'gasLimitBoundDivisor', hardfork)))
200 const maxGasLimit = pGasLimit.add(a)
201 const minGasLimit = pGasLimit.sub(a)
202
203 return gasLimit.lt(maxGasLimit) && gasLimit.gt(minGasLimit) && gasLimit.gte(this._common.param('gasConfig', 'minGasLimit', hardfork))
204}
205
206/**
207 * Validates the entire block header
208 * @method validate
209 * @param {Blockchain} blockChain the blockchain that this block is validating against
210 * @param {Bignum} [height] if this is an uncle header, this is the height of the block that is including it
211 * @param {Function} cb the callback function. The callback is given an `error` if the block is invalid
212 */
213BlockHeader.prototype.validate = function (blockchain, height, cb) {
214 var self = this
215 if (arguments.length === 2) {
216 cb = height
217 height = false
218 }
219
220 if (this.isGenesis()) {
221 return cb()
222 }
223
224 // find the blocks parent
225 blockchain.getBlock(self.parentHash, function (err, parentBlock) {
226 if (err) {
227 return cb('could not find parent block')
228 }
229
230 self.parentBlock = parentBlock
231
232 var number = new BN(self.number)
233 if (number.cmp(new BN(parentBlock.header.number).iaddn(1)) !== 0) {
234 return cb('invalid number')
235 }
236
237 if (height) {
238 var dif = height.sub(new BN(parentBlock.header.number))
239 if (!(dif.cmpn(8) === -1 && dif.cmpn(1) === 1)) {
240 return cb('uncle block has a parent that is too old or to young')
241 }
242 }
243
244 if (!self.validateDifficulty(parentBlock)) {
245 return cb('invalid Difficulty')
246 }
247
248 if (!self.validateGasLimit(parentBlock)) {
249 return cb('invalid gas limit')
250 }
251
252 if (utils.bufferToInt(parentBlock.header.number) + 1 !== utils.bufferToInt(self.number)) {
253 return cb('invalid heigth')
254 }
255
256 if (utils.bufferToInt(self.timestamp) <= utils.bufferToInt(parentBlock.header.timestamp)) {
257 return cb('invalid timestamp')
258 }
259
260 const hardfork = self._common.hardfork() ? self._common.hardfork() : self._common.activeHardfork(height)
261 if (self.extraData.length > self._common.param('vm', 'maxExtraDataSize', hardfork)) {
262 return cb('invalid amount of extra data')
263 }
264
265 cb()
266 })
267}
268
269/**
270 * Returns the sha3 hash of the blockheader
271 * @method hash
272 * @return {Buffer}
273 */
274BlockHeader.prototype.hash = function () {
275 return utils.rlphash(this.raw)
276}
277
278/**
279 * checks if the blockheader is a genesis header
280 * @method isGenesis
281 * @return {Boolean}
282 */
283BlockHeader.prototype.isGenesis = function () {
284 return this.number.toString('hex') === ''
285}
286
287/**
288 * turns the header into the canonical genesis block header
289 * @method setGenesisParams
290 */
291BlockHeader.prototype.setGenesisParams = function () {
292 this.timestamp = this._common.genesis().timestamp
293 this.gasLimit = this._common.genesis().gasLimit
294 this.difficulty = this._common.genesis().difficulty
295 this.extraData = this._common.genesis().extraData
296 this.nonce = this._common.genesis().nonce
297 this.stateRoot = this._common.genesis().stateRoot
298 this.number = Buffer.from([])
299}