UNPKG

6.89 kBJavaScriptView Raw
1const utils = require('ethereumjs-util')
2const params = require('ethereum-common/params.json')
3const BN = utils.BN
4 /**
5 * An object that repersents the block header
6 * @constructor
7 * @param {Array} data raw data, deserialized
8 * @prop {Buffer} parentHash the blocks' parent's hash
9 * @prop {Buffer} uncleHash sha3(rlp_encode(uncle_list))
10 * @prop {Buffer} coinbase the miner address
11 * @prop {Buffer} stateRoot The root of a Merkle Patricia tree
12 * @prop {Buffer} transactionTrie the root of a Trie containing the transactions
13 * @prop {Buffer} receiptTrie the root of a Trie containing the transaction Reciept
14 * @prop {Buffer} bloom
15 * @prop {Buffer} difficulty
16 * @prop {Buffer} number the block's height
17 * @prop {Buffer} gasLimit
18 * @prop {Buffer} gasUsed
19 * @prop {Buffer} timestamp
20 * @prop {Buffer} extraData
21 * @prop {Array.<Buffer>} raw an array of buffers containing the raw blocks.
22 */
23var BlockHeader = module.exports = function (data) {
24 var fields = [{
25 name: 'parentHash',
26 length: 32,
27 default: utils.zeros(32)
28 }, {
29 name: 'uncleHash',
30 default: utils.SHA3_RLP_ARRAY
31 }, {
32 name: 'coinbase',
33 length: 20,
34 default: utils.zeros(20)
35 }, {
36 name: 'stateRoot',
37 length: 32,
38 default: utils.zeros(32)
39 }, {
40 name: 'transactionsTrie',
41 length: 32,
42 default: utils.SHA3_RLP
43 }, {
44 name: 'receiptTrie',
45 length: 32,
46 default: utils.SHA3_RLP
47 }, {
48 name: 'bloom',
49 default: utils.zeros(256)
50 }, {
51 name: 'difficulty',
52 default: new Buffer([])
53 }, {
54 name: 'number',
55 default: utils.intToBuffer(params.homeSteadForkNumber.v)
56 }, {
57 name: 'gasLimit',
58 default: new Buffer('ffffffffffffff', 'hex')
59 }, {
60 name: 'gasUsed',
61 empty: true,
62 default: new Buffer([])
63 }, {
64 name: 'timestamp',
65 default: new Buffer([])
66 }, {
67 name: 'extraData',
68 allowZero: true,
69 empty: true,
70 default: new Buffer([])
71 }, {
72 name: 'mixHash',
73 default: utils.zeros(32)
74 // length: 32
75 }, {
76 name: 'nonce',
77 default: new Buffer([]) // sha3(42)
78 }]
79 utils.defineProperties(this, fields, data)
80}
81
82/**
83 * Returns the canoncical difficulty of the block
84 * @method canonicalDifficulty
85 * @param {Block} parentBlock the parent `Block` of the this header
86 * @return {BN}
87 */
88BlockHeader.prototype.canonicalDifficulty = function (parentBlock) {
89 const blockTs = new BN(this.timestamp)
90 const parentTs = new BN(parentBlock.header.timestamp)
91 const parentDif = new BN(parentBlock.header.difficulty)
92 const minimumDifficulty = new BN(params.minimumDifficulty.v)
93 var offset = parentDif.div(new BN(params.difficultyBoundDivisor.v))
94 var dif
95
96 if (this.isHomestead()) {
97 // homestead
98 // 1 - (block_timestamp - parent_timestamp) // 10
99 var a = blockTs.sub(parentTs).idivn(10).ineg().iaddn(1)
100 var cutoff = new BN(-99)
101 // MAX(cutoff, a)
102 if (cutoff.cmp(a) === 1) {
103 a = cutoff
104 }
105 dif = parentDif.add(offset.mul(a))
106 } else {
107 // prehomestead
108 if (parentTs.addn(params.durationLimit.v).cmp(blockTs) === 1) {
109 dif = offset.add(parentDif)
110 } else {
111 dif = parentDif.sub(offset)
112 }
113 }
114
115 var exp = new BN(this.number).idivn(100000).isubn(2)
116 if (!exp.isNeg()) {
117 dif.iadd(new BN(2).pow(exp))
118 }
119
120 if (dif.cmp(minimumDifficulty) === -1) {
121 dif = minimumDifficulty
122 }
123
124 return dif
125}
126
127/**
128 * checks that the block's `difficuly` matches the canonical difficulty
129 * @method validateDifficulty
130 * @param {Block} parentBlock this block's parent
131 * @return {Boolean}
132 */
133BlockHeader.prototype.validateDifficulty = function (parentBlock) {
134 const dif = this.canonicalDifficulty(parentBlock)
135 return dif.cmp(new BN(this.difficulty)) === 0
136}
137
138/**
139 * Validates the gasLimit
140 * @method validateGasLimit
141 * @param {Block} parentBlock this block's parent
142 * @returns {Boolean}
143 */
144BlockHeader.prototype.validateGasLimit = function (parentBlock) {
145 const pGasLimit = new BN(parentBlock.header.gasLimit)
146 const gasLimit = new BN(this.gasLimit)
147 const a = pGasLimit.div(new BN(params.gasLimitBoundDivisor.v))
148 const maxGasLimit = pGasLimit.add(a)
149 const minGasLimit = pGasLimit.sub(a)
150
151 return gasLimit.lt(maxGasLimit) && gasLimit.gt(minGasLimit) && gasLimit.gte(params.minGasLimit.v)
152}
153
154/**
155 * Validates the entire block header
156 * @method validate
157 * @param {Blockchain} blockChain the blockchain that this block is validating against
158 * @param {Bignum} [height] if this is an uncle header, this is the height of the block that is including it
159 * @param {Function} cb the callback function. The callback is given an `error` if the block is invalid
160 */
161BlockHeader.prototype.validate = function (blockchain, height, cb) {
162 var self = this
163 if (arguments.length === 2) {
164 cb = height
165 height = false
166 }
167
168 if (this.isGenesis()) {
169 return cb()
170 }
171
172 // find the blocks parent
173 blockchain.getBlock(self.parentHash, function (err, parentBlock) {
174 if (err) {
175 return cb('could not find parent block')
176 }
177
178 self.parentBlock = parentBlock
179
180 var number = new BN(self.number)
181 if (number.cmp(new BN(parentBlock.header.number).iaddn(1)) !== 0) {
182 return cb('invalid number')
183 }
184
185 if (height) {
186 var dif = height.sub(new BN(parentBlock.header.number))
187 if (!(dif.cmpn(8) === -1 && dif.cmpn(1) === 1)) {
188 return cb('uncle block has a parent that is too old or to young')
189 }
190 }
191
192 if (!self.validateDifficulty(parentBlock)) {
193 return cb('invalid Difficulty')
194 }
195
196 if (!self.validateGasLimit(parentBlock)) {
197 return cb('invalid gas limit')
198 }
199
200 if (utils.bufferToInt(parentBlock.header.number) + 1 !== utils.bufferToInt(self.number)) {
201 return cb('invalid heigth')
202 }
203
204 if (utils.bufferToInt(self.timestamp) <= utils.bufferToInt(parentBlock.header.timestamp)) {
205 return cb('invalid timestamp')
206 }
207
208 if (self.extraData.length > params.maximumExtraDataSize.v) {
209 return cb('invalid amount of extra data')
210 }
211
212 cb()
213 })
214}
215
216/**
217 * Returns the sha3 hash of the blockheader
218 * @method hash
219 * @return {Buffer}
220 */
221BlockHeader.prototype.hash = function () {
222 return utils.rlphash(this.raw)
223}
224
225/**
226 * checks if the blockheader is a genesis header
227 * @method isGenesis
228 * @return {Boolean}
229 */
230BlockHeader.prototype.isGenesis = function () {
231 return this.number.toString('hex') === ''
232}
233
234/**
235 * Determines if a given block part of homestead or not
236 * @method isHomestead
237 * @return Boolean
238 */
239BlockHeader.prototype.isHomestead = function () {
240 return utils.bufferToInt(this.number) >= params.homeSteadForkNumber.v
241}
242
243/**
244 * Determines if a given block part of Homestead Reprice (EIP150) or not
245 * @method isHomesteadReprice
246 * @return Boolean
247 */
248BlockHeader.prototype.isHomesteadReprice = function () {
249 return utils.bufferToInt(this.number) >= params.homesteadRepriceForkNumber.v
250}